Sorting the country select in Shopp

This came up recently while working on a site that had a bunch of target markets selected in Shopp. The country dropdown list on the Checkout page was in a somewhat random order, and I realized that it was displaying them in the order they were selected or saved. When I checked 3 new countries, they showed up at the bottom of the list. So, knowing that, we decided it would be better for everyone if we sorted the list alphabetically. The code is below:

/* Sort Countries in dropdown alphabetically */
add_filter( 'shopp_countries', 'my_country_sort' );
function my_country_sort( $_ ) {

	$countries = array_msort($_, array( 'name' => SORT_ASC ) );
	// Åland Islands (EUR) sits at the bottom, so move it to the top
	$countries = array('AX' => $countries['AX']) + $countries;
	return $countries;

}

You’ll note the comment about Åland Islands; ideally we want it to be listed with the other A countries, but I don’t know if there’s a way to sort special characters as their originating character in PHP. So, we pop it off the end of the array and push it back to the top.

Adding a date picker to Shopp

Every once in a while I get asked to add a date picker to the checkout page of a Shopp e-commerce site, usually to allow the customer to choose a delivery date for the order. There are a ton of date picker scripts out there, including a jQuery UI Datepicker (which I find ugly and difficult to reskin without adding a lot of extra bulk to your site), so I’m not going to list them all here. For my purposes, I use pickadate.js because it is lightweight, uses jQuery (which Shopp also uses), and looks great out of the box.

The first thing you’ll need to do is enable theme templates, if you haven’t already done so. Once that’s done, open your checkout.php file and add the following wherever you want your date picker field to be:

<label for="delivery-date">Delivery Date</label>
<?php 
shopp('checkout.order-data', array(
	'name' => 'Delivery Date',
	'type' => 'text',
	'id' => 'datepicker')
);
?>

This creates the input field you’re going to attach the date picker to, and gives it an id of “datepicker”. Next you’ll need to download the pickadate.js package and drop the .js and .css files into your theme folder. Then edit your theme’s functions.php file by adding the following:

add_action( 'wp_enqueue_scripts', 'pickadate_scripts' );
function pickadate_scripts() {
	// only load the date picker on the checkout page
	if ( is_shopp_checkout() ) {
		wp_enqueue_script( 'picker', get_stylesheet_directory_uri() . '/js/picker.js', 'jquery' );
		wp_enqueue_script( 'picker-date', get_stylesheet_directory_uri() . '/js/picker.date.js', array( 'jquery', 'picker' ) );
		wp_enqueue_style( 'picker', get_stylesheet_directory_uri() . '/css/picker-default.css' );
		wp_enqueue_style( 'picker-date', get_stylesheet_directory_uri() . '/css/picker-default.date.css' );
	}
}

Note the paths I’ve used; I usually put all of my theme’s CSS and JS files into separate folders so you may need to change your paths if you put them elsewhere. Now that the pickadate.js files are queued to show up on the checkout page, you need to attach the date picker to the field you added to the checkout page. This should also go in your functions.php file:

// Date picker for checkout pages
add_action( 'wp_head', 'pickadate_inline' );
function pickadate_inline () {
	if ( is_shopp_checkout() ) {
	echo "
	<script>
		jQuery(document).ready(function($) {
			$('#datepicker').pickadate();
		});
	</script>";
	}
}

It’s really that simple. If you need to modify the date picker behavior, take a look at the date picker documentation.

Disabling Multiple Clicks to the Submit Order button in Shopp

Most of us who use web browsers understand that the web UI only requires a single click on links, form buttons, and input fields. However, there are people who are used to double-clicking everything in the same way you double-click icons in an OS GUI. Sometimes this causes problems, specifically with shopping carts, where double-clicking can lead to the checkout form being submitted twice which results in duplicate orders.

I ran into this problem with Shopp, in a very specific situation where the server was responding slowly. Normally this kind of thing doesn’t happen, and I couldn’t reproduce the problem myself, but a few customers over a couple of days had managed to place duplicate orders. After checking the database I noticed that the transactions were usually 2-15 seconds apart, so I thought I’d disable the “Submit Order” button on the checkout form after it had been clicked once. Easier said than done.

Unfortunately, this isn’t as simple as using the disabled attribute on the button. Since there is some Javascript input validation, I needed to make sure that the form was validating first before disabling the button. Fortunately, I had spoken to the lead developer of Shopp about this a while back, so I knew that I needed to check `shopp.validate` which fires when the Submit Order button is clicked. If it validates correctly, then it submits the form data to the server. After some digging in the JS, I found the validation method and wrote some code to disable it after the button has been clicked once. Here’s the code:

<script type="text/javascript">// <![CDATA[
jQuery(document).ready(function($){
	var form = $("#checkout.shopp"),
		checkoutButton = $('.payoption-0 input');

	form.on("submit.validate", function(e) {
		if ( validate(this) ) {
			form.unbind('shopp_validate');
			checkoutButton.unbind("submit.validate").attr("disabled", "disabled").attr("value", "Processing...");
			// set a timer to re-enable the button or maybe reload the page
			setTimeout(timeouterr, 30000);
		}
	});

	function timeouterr() {
		$('.payoption-0').append('An error has occurred, and your order has not been sent in 30 seconds. Please <a href="javascript:location.reload();">reload the page</a> and try your transaction again.');
	}
});
// ]]></script>

All you have to do is drop this code into the bottom of the checkout.php file in your theme/shopp directory.

The code runs when the “Submit Order” button is clicked, and it checks to make sure the form is valid. If it is, it disables the validate function, which posts the data to the server, and then it changes the button text to “Processing…” so the customer knows something is happening. In the event that something doesn’t happen after 30 seconds, the `setTimeout()` function will display a message below the button that says the order hasn’t gone through, and shows a link to reload the page.

This code has stopped the duplicate transaction issue I was seeing, and it offers a better user experience especially on slow sites where a customer might click on the submit button because it doesn’t look like anything is happening.

Email Marketing Win

I just wrapped up online registration for the Mac-A-Thon which is a fundraising race for Keoua Canoe Club. This race has been going on for the past 32 years, and is one of the oldest 5k/10k runs on the island.

Three years ago when I joined the canoe club I volunteered to help with the web site. This eventually extended to helping with registration and marketing of the Mac-A-Thon. In 2011 I designed a new flyer, re-did the registration forms to include a line for an email address, and in 2012 I setup online race registration. That year, I imported all of the email addresses from our 2011 registration forms and sent out an email notice about the online registration option. We pre-registered about half of the runners online in 2012, which I took as a great step forward.

This year, I started our email marketing campaign earlier and sent more emails letting people know about our pre-registration savings deadlines. The result was a 100% increase in online registrations in 2013, which in turn makes this year one of the biggest Mac-A-Thon’s we’ve had in years.

All this with no budget, in my free time. Winning!

A huge shout out to MailChimp for making it possible.

Elavon Gateway Requires ‘ssl_customer_code’ for American Express

After building an Elavon gateway for Shopp, one of my clients reported an error saying that they were getting an error whenever one of their customers tried to pay with an American Express card. The error code was 4009 – Required Field Not Supplied, and the required field was “ssl_customer_code” which according to the developer guide is not required.

Elavon ssl_customer_code is NOT required
‘ssl_customer_code’ is NOT required… Or is it?

I’d email Elavon to let them know about the problem, but I’ve never gotten an email response from their tech support (even after talking to one of their support staff on the phone, who asked me to email him directly).

The easiest fix is to include ssl_customer_code on all transactions, even though it’s only used in  American Express transactions and (maybe?) recurring billing transactions. I hope this helps someone.

Filtering excerpts in WordPress

By default WordPress displays […] at the end of an excerpt, which doesn’t look good. Instead, I use this functions on blogs that I build, which changes it to something that looks better. Just add the following to your active theme’s functions.php:

function new_excerpt_more( $more ) {
return '... <a class="moretag" href="'. get_permalink($post-&gt;ID) . '"> Read More »</a>';
}
add_filter('excerpt_more', 'new_excerpt_more');