Implementing this feature for free extensions is very easy, instead Joomla Update System for paid extensions is a little bit complicated. As the official Joomla! site has not published a standard way to achieve this result, all developers use own method.

The first step is to choose which is your Joomla extensions manager download, the extension you will use on your website to allow customers to buy and download your own components, modules and plugins.


How to provide updates for extensions in Joomla

joomla update system for paid extensions

Virtuemart downloadable products

In this article we will speak about Virtuemart and its dedicated extension, Shipment for virtual products from Istraxx. As Virtuemart is so popular ecommerce solution for Joomla, and its community is so active, we have decided to base our Joomla delivery extension system on it.

Joomla has a special column in its update sites table called extra query, which allows to save an extra query url to be used during the upgrading process. In this Joomla extra query column, we have to write an url that should contains our website url, Virtuemart order password, Virtuemart order ID and Virtuemart media ID. All together those items will allow to download the Joomla extension, if the user has properly purchased it before, and if the order password is entered in the extension.

joomla update sites extra query


To do on your website:

Of course the first step is to install and configure both Virtuemart and Istraxx plugin.

Then, as we want to not confuse the end users, you have to create a string made by order ID and order password (we will decode it into own extensions). We have decided to join the two parts of the string by an underscore. So the final result should be like this "123_p_abcdefg" where "123" is the order number and "p_abcdefg" is the corrisponding order password, not encrypted.

Once you have that string, you have to print in the Virtuemart order page, so the customer who has bought your product, will be able to see and save that update key.

virtuemart order password istraxx download

To do on your extensions:

The best way to create an universal script able to enter the Joomla extra query for your own extensions, is to make a custom field and load it in the extension main form (for modules or plugins) or in config form (for components).

As we have already thought about how to create this custom field, you can find the explained code below.

Then, in the form where you will insert your new custom field, you have to set up also a text field where the end users can ente own order password. You will use that password in the custom field, to create the extra query.

To do on your updates server:

The last operation to do online is a little change in the XML update file hosted on your server, used from Joomla! Update System to verify if updates are available.

As Istraxx download plugin needs the Virtuemart media ID in its url, to allow the end user to download the extension, we have to add in the XML the new tag <mediaid> and read it from the previously created custom field (for example <mediaid>10</mediaid>). It will not desturb the Joomla! Update System, and it will helps us to build the Joomla update query.

deploying an update server joomla

Here the code for the Joomla custom field used to obtain extra query for paid extensions:

class JFormFieldUpdatesites extends JFormField {
    protected $type = 'updatesites';

    protected function getLabel() {
        return;
    }
    
    protected function getInput() {
        
        // get the component information from #__extensions table
        JLoader::import('joomla.application.component.helper');
        $component = JComponentHelper::getComponent('com_NAME_OF_YOUR_EXTENSION');
        $component_id = $component->id;
        
        // website parameters
        $extra_query = 'index.php?option=com_virtuemart&view=plugin&name=istraxx_download';
        $xml_link = ' HERE THE UPDATE XML URL ON YOUR SERVER ';
        
        // get media id from xml file from your server
        $xml_file = simplexml_load_file($xml_link) or die('Sorry XML file cannot be loaded.');
        $media_id = $xml_file->update->mediaid;
        
        // get order password from component parameters
        $credentials = $component->params->get('order_password', '');
        
        // build order variables
        $credentials_array = explode('_', $credentials, 2);
        $order_item_id = $credentials_array[0];
        $order_password = $credentials_array[1];
        
        // build the extra query
        if (!empty($media_id) && !empty($order_item_id) && !empty($order_password))
        {
            $extra_query .= '&media_id='.$media_id.'&dlkey='.base64_encode($order_password).'&oid='.$order_item_id;
        }
        
        // load the update site record, if it exists
        $db = JFactory::getDbo();
        $query = $db->getQuery(true)
            ->select('update_site_id')
            ->from($db->qn('#__update_sites_extensions'))
            ->where($db->qn('extension_id').' = '.$db->q($component_id));
        $db->setQuery($query);
        $update_site = $db->loadResult();
        
        if ($update_site)
        {
            // update the record in upload sites table
            $update_site_fields = array(
                $db->qn('extra_query') . ' = ' . $db->quote($extra_query),
                $db->qn('enabled') . ' = 1',
                $db->qn('last_check_timestamp') . ' = 0'
            );
            $query = $db->getQuery(true)
                ->update($db->qn('#__update_sites'))
                ->set($update_site_fields)
                ->where($db->qn('update_site_id').' = '.$update_site);
            $db->setQuery($query);
            $db->execute();

            // purge the updates cache for this update site
            $query = $db->getQuery(true)
                ->delete($db->qn('#__updates'))
                ->where($db->qn('update_site_id').' = '.$update_site);
            $db->setQuery($query);
            $db->execute();
        }
        else
        {
            // enter the update site in database
            $update_site_columns = array('update_site_id', 'name', 'type', 'location', 'extra_query', 'enabled', 'last_check_timestamp');   
            $update_site_values = array('DEFAULT', $db->quote('NAME OF YOUR EXTENSION'), $db->quote('extension'), $db->quote($xml_link), $db->quote($extra_query), 1, 0);
            $query = $db->getQuery(true)
                ->insert($db->qn('#__update_sites'))
                ->columns($db->qn($update_site_columns))
                 ->values(implode(',', $update_site_values));
            $db->setQuery($query);
            $db->execute();
            
            // load the update site id previously created
            $query = $db->getQuery(true)
                ->select('update_site_id')
                ->from($db->qn('#__update_sites'))
                ->where($db->qn('name').' = '.$db->q('NAME OF YOUR EXTENSION'))
                ->order($db->qn('update_site_id').' DESC')
                ->setLimit(1);
            $db->setQuery($query);
            $update_site_id = $db->loadResult();
            
            // enter the extension id to allow joomla to know for which extension the update site is made
            $update_sites_extensions_columns = array('update_site_id', 'extension_id');   
            $update_sites_extensions_values = array($update_site_id, $component_id);
            $query = $db->getQuery(true)
                ->insert($db->qn('#__update_sites_extensions'))
                ->columns($db->qn($update_sites_extensions_columns))
                 ->values(implode(',', $update_sites_extensions_values));
            $db->setQuery($query);
            $db->execute();
        }
        
        $update_button = '< a onclick="jQuery(\'#toolbar-apply\').children(\'button\').click()">Update password< /a>';

        return $update_button;
    }
    
}