Pricing tables are a key component of your business that promotes your products and helps users choose between different services you have. Most modern commercial WordPress themes provide built in Pricing Tables. There are also plenty of free and commercial pricing table plugins for WordPress. This tutorial is intended to provide knowledge to WordPress developers on creating a plugin from scratch which enables customization in different projects.
Every web design is trying to accomplish responsive features which enable better look and feel on any kind of device. The pricing tables created with this plugin will work on all kinds of devices such as mobiles and tablets as well. So let's get started.
You can take a look at the final design of the pricing table on the following screen.
Planning the Pricing Table Plugin
Planning is the difficult part in any project. It's better to plan how we are going to develop the pricing table and what techniques we are going to use before doing any coding. Basically the pricing table will have the following components:
-
Pricing Table - is a collection of different kinds of packages available for your product or service. Since we are planning to create multiple pricing tables, it's better to choose a Custom Post Type called
pricing_tables
. -
Pricing Package - is a collection of features in your product which varies across different packages. Since pricing tables can contain multiple packages, a Custom Post Type called
pricing_packages
will be used. - Package Features - are a collection of unique elements of your product or service. Features will be added dynamically using custom fields in the process of creating a pricing package.
- Pricing Table Design - we can choose either a page template or shortcode to display the pricing tables. We will be using a shortcode in this plugin.
Creating Pricing Packages
We are going to use a custom post type called pricing_packages
. You can simply generate code for creating a custom post type using an online code generator. The following code creates the custom post type for packages using the default configurations:
add_action( 'init', 'wppt_register_cpt_pricing_packages' ); function wppt_register_cpt_pricing_packages() { $labels = array( 'name' => _x( 'Pricing Packages', 'pricing_packages' ), 'singular_name' => _x( 'Pricing Package', 'pricing_packages' ), 'add_new' => _x( 'Add New', 'pricing_packages' ), 'add_new_item' => _x( 'Add New Pricing Package', 'pricing_packages' ), 'edit_item' => _x( 'Edit Pricing Package', 'pricing_packages' ), 'new_item' => _x( 'New Pricing Package', 'pricing_packages' ), 'view_item' => _x( 'View Pricing Package', 'pricing_packages' ), 'search_items' => _x( 'Search Pricing Packages', 'pricing_packages' ), 'not_found' => _x( 'No Pricing Packages found', 'pricing_packages' ), 'not_found_in_trash' => _x( 'No Pricing Packages found in Trash', 'pricing_packages' ), 'parent_item_colon' => _x( 'Parent Pricing Package:', 'pricing_packages' ), 'menu_name' => _x( 'Pricing Packages', 'pricing_packages' ), ); $args = array( 'labels' => $labels, 'hierarchical' => false, 'description' => 'Pricing Packages', 'supports' => array( 'title', 'editor' ), 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'show_in_nav_menus' => true, 'publicly_queryable' => true, 'exclude_from_search' => false, 'has_archive' => true, 'query_var' => true, 'can_export' => true, 'rewrite' => true, 'capability_type' => 'post' ); register_post_type( 'pricing_packages', $args ); }
Using Custom Fields for Package Information
Package name, price and link to purchase are the most important components in a pricing package. So we will be using a meta box to add these fields. Also we require multiple features for a package. We are going to use another meta box to add and remove features dynamically as shown in the code below.
add_action( 'add_meta_boxes', 'wppt_pricing_packages_meta_boxes' ); function wppt_pricing_packages_meta_boxes() { add_meta_box( "pricing-package-info", "Pricing Package Info", 'wppt_generate_pricing_package_info', "pricing_packages", "normal", "high" ); add_meta_box( "pricing-features-info", "Pricing Features", 'wppt_generate_pricing_features_info', "pricing_packages", "normal", "high" ); }
Meta boxes for package fields are added using the add_meta_boxes
action. You can use one meta box instead of two. I have used two meta boxes to make things clear.
function wppt_generate_pricing_package_info() { global $post; $package_price = get_post_meta( $post->ID, "_package_price", true ); $package_buy_link = get_post_meta( $post->ID, "_package_buy_link", true ); $html = '<input type="hidden" name="pricing_package_box_nonce" value="' . wp_create_nonce( basename( __FILE__ ) ) . '" />'; $html .= '<table class="form-table">'; $html .= '<tr>'; $html .= ' <th><label for="Price">Package Price *</label></th>'; $html .= ' <td>'; $html .= ' <input name="package_price" id="package_price" type="text" value="' . $package_price . '" />'; $html .= ' </td>'; $html .= '</tr>'; $html .= '<tr>'; $html .= ' <th><label for="Buy Now">Buy Now Link *</label></th>'; $html .= ' <td>'; $html .= ' <input name="package_buy_link" id="package_buy_link" type="text" value="' . $package_buy_link . '" />'; $html .= ' </td>'; $html .= '</tr>'; $html .= '</tr>'; $html .= '</table>'; echo $html; }
First meta box will contain fields for price and purchase link. Those 2 fields will be saved in post_meta
table using _package_price
and _package_buy_link
keys respectively.
function wppt_generate_pricing_features_info() { global $post; $package_features = get_post_meta( $post->ID, "_package_features", true ); $package_features = ( $package_features == '' ) ? array() : json_decode( $package_features ); $html .= '<table class="form-table">'; $html .= '<tr><th><label for="Price">Add Package Features</label></th>'; $html .= ' <td> $html .= ' <input name="package_feature" id="package_feature" type="text" />'; $html .= ' <input type="button" id="add_features" value="Add Features" />'; $html .= ' </td></tr>'; $html .= '<tr><td>'; $html .= ' <ul id="package_features_box" name="package_features_box" >'; foreach ($package_features as $package_feature) { $html .= '<li><input type="hidden" name="package_features[]" value="' . $package_feature . '" />' . $package_feature . ''; $html .= '<a href="javascript:void(0);">Delete</a></li>'; } $html .= '</ul></td></tr>'; $html .= '</table>'; echo $html; }
The above code contains the HTML for the package features fields. One package will contain many features, hence the features will be added dynamically to an unordered list with hidden fields using jQuery. Features will be stored in the post_meta
table with _package_features
key. A JSON formatted string will be used to keep multiple features.
Validating Package Creation
Package price and purchase link is mandatory for every package. So we need some validation code before creating packages. We will be using jQuery code to validation as shown below.
jQuery(document).ready(function($) { $("#post-body-content").prepend('<div id="pricing_error" class="error" style="display:none" ></div>'); $('#post').submit(function() { if ( $("#post_type").val() =='pricing_packages' ) { return wppt_validate_pricing_packages(); } else if ( $("#post_type").val() =='pricing_tables' ) { return wppt_validate_pricing_tables(); } }); });
We need to add a div
element to the post creation screen to display errors. The post creation screen contains a form with the ID post. It does not change for the custom post types either. So I have used the submit event of the #post
form for validation.
Since we are using different custom post types, it's necessary to filter the post type using the code below. Post creation form contains a hidden field with the ID post_type
.
if ( $("#post_type").val() =='pricing_packages') { return wppt_validate_pricing_packages(); } else if ( $("#post_type").val() =='pricing_tables' ) { return wppt_validate_pricing_tables(); }
Once the filtering is completed we call a specific validation function according to the post type as shown below.
var wppt_validate_pricing_packages = function() { var err = 0; $("#pricing_error").html(""); $("#pricing_error").hide(); if ( $("#title").val() == '' ) { $("#pricing_error").append( "<p>Please enter Package Name.</p>" ); err++; } if ( $("#package_price").val() == '' ) { $("#pricing_error").append( "<p>Please enter Package Price.</p>" ); err++; } if ( $("#package_buy_link").val() == '' ) { $("#pricing_error").append( "<p>Please enter Package Buy Link.</p>" ); err++; } if ( err > 0 ) { $("#publish").removeClass( "button-primary-disabled" ); $("#ajax-loading").hide(); $("#pricing_error").show(); return false; } else { return true; } };
First we clear any existing errors set previously inside the pricing_error
element. Then we can validate each field and add the error message to the pricing_error
container. Finally we display the errors using the $("#pricing_error").show()
function.
$("#publish").removeClass("button-primary-disabled")
. Also we can stop the ajax loading image using $("#ajax-loading").hide()
.
Save Package Information
Now we have completed all the field creations and validations for packages. Let's move on to saving the information to the database. We will be using the save_post
action which executes just after the post is saved to the database.
add_action( 'save_post', 'wppt_save_pricing_packages' ); function wppt_save_pricing_packages( $post_id ) { if ( ! wp_verify_nonce( $_POST['pricing_package_box_nonce'], basename( __FILE__ ) ) ) { return $post_id; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } if ( 'pricing_packages' == $_POST['post_type'] && current_user_can( 'edit_post', $post_id ) ) { $package_price = ( isset( $_POST['package_price'] ) ? $_POST['package_price'] : '' ); $package_buy_link = ( isset( $_POST['package_buy_link'] ) ? $_POST['package_buy_link'] : '' ); $package_features = ( isset( $_POST['package_features'] ) ? $_POST['package_features'] : array() ); $package_features = json_encode( $package_features ); update_post_meta( $post_id, "_package_price", $package_price ); update_post_meta( $post_id, "_package_buy_link", $package_buy_link ); update_post_meta( $post_id, "_package_features", $package_features ); } else { return $post_id; } }
First we do some validations to check if the nonce value generated in the form matches with the nonce value received in $_POST
. Next we have to make sure whether it's an auto save or manual save. Next we have to check the post type before saving. Otherwise this code will be executed on every type of post. Package price and purchase link will be saved directly in the post_meta
table using the update_post_meta
function. Features will be saved as a JSON encoded string. Now we are done with creating packages. Let's move on to creating pricing tables.
Creating Pricing Tables
Pricing tables are going to be another custom post type on our plugin. Custom Post Type creation and saving to the database is very similar to the code explained previously. So I am not going to make it boring by explaining the same thing. You can find custom post creation and saving code for pricing tables in the plugin files.
Pricing Tables contains pricing packages. Hence the meta boxes used for pricing tables are going to be different from one that we used for pricing packages. Let's take a look at the meta box creation for pricing tables.
add_action( 'add_meta_boxes', 'wppt_pricing_tables_meta_boxes' ); function wppt_pricing_tables_meta_boxes() { add_meta_box( "pricing-table-info", "Pricing Table Info", 'wppt_generate_pricing_table_info', "pricing_tables", "normal", "high" ); } function wppt_generate_pricing_table_info() { global $post; $table_packages = get_post_meta( $post->ID, "_table_packages", true ); $table_packages = ( $table_packages == '' ) ? array() : json_decode( $table_packages ); $query = new WP_Query( array( 'post_type' => 'pricing_packages', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'post_date', 'order' => 'ASC' ) ); $html = '<input type="hidden" name="pricing_table_box_nonce" value="' . wp_create_nonce( basename( __FILE__ ) ) . '" />'; $html .= '<table class="form-table">'; $html .= '<tr><th>Package Status</th>'; $html .= ' <td>Package Name</td></tr>'; while ( $query->have_posts() ) : $query->the_post(); $checked_status = ( in_array( $query->post->ID, $table_packages ) ) ? "checked" : ""; $html .= '<tr><th><input type="checkbox" name="pricing_table_packages[]" ' . $checked_status . ' value="' . $query->post->ID . '" /></th>'; $html .= ' <td>' . $query->post->post_title . '</td></tr>'; endwhile; $html .= '</table>'; echo $html; }
We can add a custom meta box for pricing tables using similar code to that used in the previous section. Packages of a specific table are going to be stored in the post_meta
table as a JSON encoded string. So we get the packages for the current table using the get_post_meta
function. Then we get all the published pricing packages using a custom query as shown below.
$query = new WP_Query( array( 'post_type' => 'pricing_packages', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'post_date', 'order' => 'ASC' ) );
We can select the packages using the pricing_packages
custom post type. You can choose other conditions for the query according to your preference.
WP_Query
will return the default number of records in the result if posts_per_page
is not specified. You can get all the posts without any limit by using -1
for the posts_per_page
condition.
Then we use the result generated from WP_Query
to display all the pricing packages with checkboxes in front of them. While looping we check whether the package is already assigned to the table and assign a check to the checkbox. You can add or remove packages dynamically by selecting / deselecting the checkboxes and hitting the update button.
Validating Pricing Table Creation
We are going to validate the pricing table name like we did earlier with pricing packages. The following jQuery code will be used for validation:
var wppt_validate_pricing_tables = function() { var err = 0; $("#pricing_error").html(""); $("#pricing_error").hide(); if ( $("#title").val() == '' ) { $("#pricing_error").append( "<p>Please enter Pricing Table Name.</p>" ); err++; } if ( err > 0 ) { $("#publish").removeClass( "button-primary-disabled" ); $("#ajax-loading").hide(); $("#pricing_error").show(); return false; } else { return true; } };
The above code is included in the pricing_admin.js file in the js folder of the plugin.
Generating Pricing Table ID
We need a unique key for pricng tables in order to include them in the shortcode. So we will be using the ID of the pricing table as the key. Once the table is created we can show the pricing table ID in the list view using a custom column. WordPress provides a simple technique to include custom columns in the list view as shown below:
add_filter( 'manage_edit-pricing_tables_columns', 'wppt_edit_pricing_tables_columns' ); function wppt_edit_pricing_tables_columns( $columns ) { $columns = array( 'cb' => '<input type="checkbox" />', 'ID' => __( 'Pricing Table ID' ), 'title' => __( 'Pricing Table Name' ), 'date' => __( 'Date' ) ); return $columns; }
We can use the manage_edit-{Custom Post Type}_columns
filter to customize the columns displayed in list view. As you can see I have assigned a custom column called Pricing Table ID to use the post ID.
add_action( 'manage_pricing_tables_posts_custom_column', 'wppt_manage_pricing_tables_columns', 10, 2 ); function wppt_manage_pricing_tables_columns( $column, $post_id ) { global $post; switch ( $column ) { case 'ID' : $pricing_id = $post_id; if ( empty( $pricing_id ) ) echo __( 'Unknown' ); else printf( $pricing_id ); break; default : break; } } add_filter( 'manage_edit-pricing_tables_sortable_columns', 'wppt_pricing_tables_sortable_columns' ); function wppt_pricing_tables_sortable_columns( $columns ) { $columns['ID'] = 'ID'; return $columns; }
Then we can assign the values to the column using a switch statement on the manage_{Custom Post Type}_posts_custom_column
action. Finally we use the manage_edit-{Custom Post Type}_sortable_columns
filter to make the column sortable. After creating a pricing table you will be able to see the number in the list view.
Including Plugin Scripts and Styles
We used a custom JavaScript file for validating admin area functions. So first we are going to take a look at how script files are included in the WordPress admin area.
function wppt_pricing_admin_scripts() { wp_register_script( 'pricing-admin', plugins_url( 'js/pricing_admin.js', __FILE__ ), array( 'jquery' ) ); wp_enqueue_script( 'pricing-admin' ); } add_action( 'admin_enqueue_scripts', 'wppt_pricing_admin_scripts' );
admin_enqueue_scripts
is a hook which can be used to include all the CSS and Script files in the Admin area of WordPress. First we have to register the script using the wp_register_script
function with a unique key and path to the file. Then we can use the wp_enqueue_script
function to include the files.
Now let's take a look at the inclusion of front end styles and scripts using the code below:
function wppt_pricing_front_scripts() { wp_register_style( 'pricing-base', plugins_url( 'css/base.css', __FILE__ ) ); wp_enqueue_style( 'pricing-base' ); wp_register_style( 'pricing-layout', plugins_url( 'css/layout.css', __FILE__ ) ); wp_enqueue_style( 'pricing-layout' ); wp_register_style( 'pricing-fluid-skeleton', plugins_url( 'css/fluid_skeleton.css', __FILE__ ) ); wp_enqueue_style( 'pricing-fluid-skeleton' ); wp_register_style( 'pricing-table', plugins_url( 'css/pricing_table.css', __FILE__ ) ); wp_enqueue_style( 'pricing-table' ); } add_action( 'wp_enqueue_scripts', 'wppt_pricing_front_scripts' );
In the start of the tutorial I mentioned that we will be creating a responsive pricing table. It's easier to work with an existing CSS framework to provide responsive functionality. So I have chosen Skeleton as the CSS framework. The first three CSS files will be the Skeleton framework's CSS files, then we have included a custom style for our pricing table in the pricing_table.css file.
Now all the data needed for the pricing table is ready and we can move on to creating the pricing table design.
Designing the Pricing Table
Designing a responsive pricing table is a time consuming task and requires advanced knowledge in HTML and CSS. Hence explaining the design is out of the scope of this tutorial. I'll be using a responsive pricing table which I created for Webdesigntuts+. If you are interested you can learn about the designing part by reading Responsive Pricing Tables Using :target
for Small Screens tutorial. The following code contains the HTML code for a pricing table with two packages:
<div class="container"> <div id='pricing_plan1' class="four columns"> <dl class='plans' > <dd class="plan_title"> Basic </dd> <dd class="plan_price"> $9.99 </dd> </dl> <dl class='plan' id="one"> <dt class='plan_more'>View<a href="#one" class="more_icon"></a><a href="#" class="less_icon"></a></dt> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">1GB</span> Storage</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">5GB</span> Bandwidth</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">2</span> Domains</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">3</span> Databases</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">1</span> FTP Account</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">25</span> Email Accounts</div> </dd> <dd class="plan_buy"> <a href='' class='buy' >Buy Now</a> </dd> </dl> </div> <div id='pricing_plan2' class="four columns"> <dl class='plans'> <dt class="plan_title"> Standard </dt> <dd class="plan_price"> $19.99 </dd> </dl> <dl class='plan' id="two"> <dt class='plan_more'>View<a href="#two" class="more_icon"></a><a href="#" class="less_icon"></a></dt> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">10GB</span> Storage</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">50GB</span> Bandwidth</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">10</span> Domains</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">25</span> Databases</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">10</span> FTP Account</div> </dd> <dd class="plan_features"> <div class="feature_desc"><span class="highlight">100</span> Email Accounts</div> </dd> <dd class="plan_buy"> <a href='' class="buy">Buy Now</a> </dd> </dl> </div> </div>
Now we have to choose a method to include the pricing table code in WordPress. We can either include this code in a shortcode or create a specific page template. I am going to use a shortcode. You can try the page template method as well.
Creating Pricing Shortocde
Shortcodes are an easy way of adding dynamic functionality to your posts and pages. I'll be using a shortcode called wppt_show_pricing
for inserting a pricing table. We have to pass the ID of the pricing table as the shortcode parameter using the pricing_id
key. Let's implement the shortcode.
add_shortcode( "wppt_show_pricing", "wppt_generate_pricing_table" ); function wppt_generate_pricing_table( $atts ) { global $post; extract( shortcode_atts( array( 'pricing_id' => '0', ), $atts ) ); // Remaining Code }
I have defined a shortcode called wppt_show_pricing
using WordPress' add_shortcode
function. Shortcode attributes will be automatically passed to the function.First we extract the shortcode array and the ID of the pricing table will be assigned to the pricing_id
variable. The remainder of the code will be explained in the following sections.
global $post; $table_packages = get_post_meta( $pricing_id, "_table_packages", true ); $table_packages = ( $table_packages == '' ) ? array() : json_decode( $table_packages );
Then we get the pricing table ID assigned to the shortcode and all the packages included in the pricing table from the post_meta
table.
$html = '<div class="container">'; $pricing_index = 0; foreach ( $table_packages as $table_package ) { $pricing_index++; $plan_title = get_the_title( $table_package ); $package_price = get_post_meta( $table_package, "_package_price", true ); $package_buy_link = get_post_meta( $table_package, "_package_buy_link", true ); $package_features = get_post_meta( $table_package, "_package_features", true ); $package_features = ( $package_features == '' ) ? array() : json_decode( $package_features ); $html .= '<div id="pricing_plan' . $pricing_index . '" class="four columns">'; $html .= '<dl class="plans"> <dd class="plan_title"> ' . $plan_title . ' </dd> <dd class="plan_price"> $' . $package_price . ' </dd> </dl>'; $html .= '<dl class="plan" id="pr' . $pricing_index . '"> <dt class="plan_more">View<a href="#pr' . $pricing_index . '" class="more_icon"></a> <a href="#" class="less_icon"></a> </dt>'; foreach ( $package_features as $package_feature ) { $html .= '<dd class="plan_features"> <div class="feature_desc"><span class="highlight">' . $package_feature . '</span></div> </dd>'; } $html .= '<dd class="plan_buy"> <a href="' . $package_price . '" class="buy" >Buy Now</a> </dd> </dl>'; $html .= '</div>'; } $html .= '</div>'; echo $html;
While traversing through each package generated in the previous code, we get the features and details of the packages using the get_post_meta
function. Then we include the HTML code with dynamic data to display the pricing table. Finally we return the generated HTML code to be displayed inside pages which contain the shortcode.
Now we have completed the development of our pricing table plugin for WordPress. You can go through the source code and add your own custom functionality according to your preferences.
Guidelines for Using Pricing Table
Anyone with the basic knowledge of working with WordPress will be able to create pricing tables using this plugin. I'll provide the steps to make it easier for users without much of an experience in WordPress.
- Click the Pricing Packages section on the left menu and create a package with name, price, purchase link and features. Continue this process until you create all your packages.
- Then click on the Pricing Tables on the left menu to add a new pricing table. A list of pricing packages will display in the bottom of the screen. Select the packages you want and save the Pricing Table with a specific name.
- Go to the pricing table list and find the pricing table ID of the newly created table.
- Then click on the Pages section to add a new page. Enter the shortcode
[wppt_show_pricing pricing_id='ID' /]
inside the page editor. Replace the ID with the actual pricing table ID. - Save the page and view it in a browser. You will get your responsive pricing table.
I hope you learnt how to use Custom Post Types and Meta Boxes to create a pricing table. Let us know if you have your own way of creating such plugin through the comments below.
Comments