Geolocated Pricing for Your WordPress Site – A Freemius integration tutorial
I released Contextual Related Posts Pro in June and followed up with Top 10 Pro in September. Both plugins use Freemius to handle licensing. Amongst other things, the benefit of going with Freemius is that I can easily offer licences in USD, GBP or EUR.
In this tutorial, you’ll learn how to set up multi-currency plans in Freemius and then configure your website’s pricing page to display pricing in the best currency as applicable automatically.
Step 1: Set up the Plans in Freemius
Once you have set up your product in Freemius, navigate to the Plans page and either Add a new plan or click on an existing one to modify it.
In the plan’s config, you’ll find a new dropdown labelled Add Currency. Click it and select the desired currency. I’ve added GBP and EUR currencies for my plugins’ plans in addition to USD.
Make a note of the currency exchange you’re using to set the prices in GBP and EUR as we’ll need to use it for our script in the next step.
Once you’ve set up the price of the license in each currency, buyers will be able to choose between these currencies on the Checkout screen. Here’s what the checkout screen for Contextual Related Posts Pro looks like with the drop-down. Since I’m in the UK, my site automatically shows currencies in GBP!
Step 2: Create the Pricing Table HTML
First, let’s create the HTML structure for our pricing table. You can add this to your plugin’s page template or use a shortcode to display it. I created mine using the block editor but set the necessary classes that I could target with JavaScript.
<div class="pricing-table">
<div class="pricing-column pro">
<h3>Pro Plan</h3>
<div class="price">
<span data-plan="pro-plan-1">$49</span>/year
</div>
<a href="#" class="buy-button pro-plan pro-plan-1">Buy Now</a>
</div>
<div class="pricing-column pro">
<h3>Pro Plan (5 licenses)</h3>
<div class="price">
<span data-plan="pro-plan-1">$99</span>/year
</div>
<a href="#" class="buy-button pro-plan pro-plan-5">Buy Now</a>
</div>
</div>
In my case, I have a Pro plan that allows a user to buy single-site or multisite licenses. To make the JavaScript dynamic, I have the classes set as pro-plan-1, pro-plan-5, etc.
Step 3: Enqueue JavaScript and Localize Script Data
Within your theme’s functions.php or any other functional plugin, add the following PHP code that will enqueue the js file we’ll build in the next step.
/**
* Enqueue pricing script and localize data.
*/
function wz_enqueue_pricing_script() {
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'freemius-checkout', 'https://checkout.freemius.com/checkout.min.js', array(), null, true );
wp_enqueue_script( 'wz-pricing', plugin_dir_url( __FILE__ ) . 'js/pricing.js', array( 'jquery', 'freemius-checkout' ), '1.0.0', true );
$script_data = array(
'pluginName' => 'WebberZone Plugin',
'pluginID' => '12345', // Your Freemius plugin ID.
'publicKey' => 'pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // Your Freemius public key.
'proID' => '12345', // Your Pro plan ID.
'agencyID' => '67890', // Your Agency plan ID.
'usdToGbp' => 0.79, // Current exchange rate.
'usdToEur' => 0.91, // Current exchange rate.
'usdPrices' => array(
'pro-plan-1' => 29,
'pro-plan-5' => 99,
),
);
wp_localize_script( 'wz-pricing', 'pluginData', $script_data );
}
add_action( 'wp_enqueue_scripts', 'wz_enqueue_pricing_script' );
Update the script data to that of your plugins as well as the exchange rate that you want to use. The usdPrices
array contains the various plans and their prices in USD.
This code enqueues jQuery, the Freemius checkout script, and your custom pricing script. It also passes important data to your JavaScript using wp_localize_script()
.
Step 4: Create the JavaScript file
Create a new file called pricing.js
in your plugin’s js
directory and add the following code:
jQuery(document).ready(function ($) {
var freemiusHandler = FS.Checkout.configure({
name: pluginData.pluginName,
plugin_id: pluginData.pluginID,
public_key: pluginData.publicKey,
currency: 'auto'
});
var plans = {
pro: pluginData.proID,
};
// Function to handle the click event for pro plans
var handleProPlanClick = function (e) {
e.preventDefault();
var classes = $(this).attr('class').split(' ');
var licenseType = classes.filter(cls => cls.startsWith('pro-plan-'))[0];
licenseType = licenseType ? parseInt(licenseType.split('-').pop()) : 0;
buyPlan('pro', licenseType);
};
// Function to buy the plan
var buyPlan = function (plan, licenseType) {
freemiusHandler.open({
plan_id: plans[plan],
licenses: licenseType,
// You can consume the response for after purchase logic.
success: function (response) {
// alert(response.user.email);
}
});
};
// Attach click event handlers to pro plan links
$('a[class*="pro-plan"]').on('click', handleProPlanClick);
// Fetch geo data and update prices.
const USD_TO_GBP = pluginData.usdToGbp;
const USD_TO_EUR = pluginData.usdToEur;
const GEO_JSON_URL = 'https://checkout.freemius.com/geo.json';
const usdPrices = pluginData.usdPrices;
// Fetch geo data
function fetchGeoData() {
return $.getJSON(GEO_JSON_URL)
.fail(function () {
// Default to USD if the request fails.
return {
'country': {
'name': 'United States',
'code': 'us'
},
'currency': {
'code': 'USD',
'symbol': '$'
}
};
});
}
// Calculate price based on currency
function calculatePrice(price, currencyCode) {
switch (currencyCode) {
case 'GBP':
return Math.floor(price * USD_TO_GBP);
case 'EUR':
return Math.ceil(price * USD_TO_EUR);
default:
return price; // USD
}
}
// Update the DOM with the calculated price
function updatePrice(planName, price, currencySymbol) {
$(`[data-plan="${planName}"]`).text(`${currencySymbol}${price}`);
}
// Check localStorage for cached geo data
function getCachedGeoData() {
const cachedData = localStorage.getItem('geo_data');
if (cachedData) {
return JSON.parse(cachedData);
}
return null;
}
// Cache geo data in localStorage
function cacheGeoData(data) {
localStorage.setItem('geo_data', JSON.stringify(data));
}
// Main function to handle fetching and displaying prices
function handlePrices() {
let geoData = getCachedGeoData();
if (geoData) {
updatePrices(geoData);
} else {
fetchGeoData().then(function (data) {
cacheGeoData(data);
updatePrices(data);
});
}
}
// Update prices based on geo data
function updatePrices(geoData) {
const currencyCode = geoData.currency.code.toUpperCase();
const currencySymbol = geoData.currency.symbol;
$.each(usdPrices, function (planName, price) {
const calculatedPrice = calculatePrice(price, currencyCode);
updatePrice(planName, calculatedPrice, currencySymbol);
});
}
handlePrices();
});
Explaining the Script
Here’s a breakdown of what the script does:
- Freemius Configuration: It sets up the Freemius checkout handler with your WebberZone plugin’s details.
- Event Handlers: It attaches click event handlers to the “Buy Now” buttons for different plans.
- Geo-based Pricing:
- Fetches geographical data from Freemius API to determine the user’s location and currency.
- Calculates prices based on predefined USD to GBP and EUR conversion rates.
- Updates the pricing table with the converted prices and appropriate currency symbols.
- Caching: Implements a simple caching mechanism using
localStorage
to store geographical data and reduce API calls directly within the user’s browser. - DOM Updates: Updates the pricing displayed on the webpage based on the calculated prices and currency.
Customisation and Best Practices
- Exchange Rates: Remember to update the exchange rates in your PHP code if you’re changing your Freemius plans to ensure there is consistency in the pricing.
- Responsive Design: Ensure your pricing table is responsive for different screen sizes.
- Testing: Thoroughly test the checkout process with Freemius sandbox mode before going live.
- Optimization: Consider selectively loading the pricing script if it’s not needed on every page. I only load this on selected page IDs on this site.
- WordPress Coding Standards:
- Use a prefix for functions to avoid naming conflicts with other plugins. I have
wz_
in the script above. - Follow WordPress naming conventions for functions and variables.
- Add appropriate inline documentation for functions.
- Use a prefix for functions to avoid naming conflicts with other plugins. I have
Conclusion
Implementing dynamic pricing with Freemius in your WordPress plugin offers a powerful way to customise your pricing based on user location and currency. This approach not only enhances the user experience by displaying prices in local currencies but also streamlines the checkout process.
By following this tutorial, you’ve learned how to set up the pricing table, handle currency conversions, and integrate Freemius checkout seamlessly. You’re well-equipped to offer a more personalised and efficient purchasing experience for your plugin users, potentially boosting conversions and customer satisfaction.
Using this script on your site, comment below and let me know your experience!