Licensable modules

Overview

If you are offering your module on the SmartStore Community Marketplace, you can easily make it licensable. A licensed module behaves as follows:

  • The module can be used for free for 30 days (demonstration mode). After this period, it has to be licensed.

  • A shop administrator receives a license key after purchasing the module on the SmartStore Marketplace. To activate the license key, it has to be entered in the administration backend (Plugins > Manage Plugins).

  • The license status is periodically checked to ensure that nobody is misusing your module.

A license key is only valid for the IP address that activated the key.

LicensableModule attribute

A module can be marked as a licensable piece of code by decorating the module class (inherited from ModuleBase) with the LicensableModuleAttribute.

There must be a dependency on the Smartstore.Web.Common project for the module to use the licensing component.

LicensableModuleAttribute has a HasSingleLicenseForAllStores option related to multi-stores. If set to True, the license key has no domain binding and is therefore valid for all stores. Recommended is the default value False, i.e. a new license key is required for each store.

License check

The module should check the license status when executing important functions and disable or restrict them if there is no valid license. It is recommended not to do this permanently but throttled via Throttle.Check.

[LicensableModule]
internal class Module : ModuleBase, IConfigurable
{
    public static string SystemName => "MyCompany.MyModule";

    public async static Task<bool> CheckLicense(bool async)
    {
        // TODO: set key. Must be globally unique!
        var key = "any random characters";
        // Check once per hour.
        var interval = TimeSpan.FromHours(1);

        if (async)
        {
            return await Throttle.CheckAsync(key, interval, true,
                async () => await LicenseChecker.CheckStateAsync(SystemName) > LicensingState.Unlicensed);
        }
        else
        {
            return Throttle.Check(key, interval, true,
                () => LicenseChecker.CheckState(SystemName) > LicensingState.Unlicensed);
        }
    }
}

LicensingState is an enum with the following values:

State
Value
Decsription

Unlicensed

0

Invalid license key or demo period expired.

Demo

10

Demonstration period valid for 30 days. After expiration, the status changes to Unlicensed. An unlimited demonstration period is given to developers on localhost.

Licensed

20

The license is valid.

The license check is always performed against the current request URL or as a fallback against the current store URL (if there is no HTTP request object, which is usually the case in background tasks). If you want to check against another domain, you have to pass the corresponding URL via the second parameter of CheckStateAsync.

License Checker

The license checker is a set of static methods for license validation within Smartstore.Licensing.dll.

Method
Description

IsLicensableModule

Gets a value indicating whether a module is a licensed piece of code where the user has to enter a license key that has to be activated.

GetLicense

Gets information about a license for a given module system name such as the remaining demo days.

Activate

Activates a license key. This is done when the user enters their license key in the administration backend.

Check

Checks the state of a license for a given module system name and returns various information about the license. If you need a stringified version of the result (German and English localization supported), call the ToString() method of the returned object.

CheckState

Same as Check or CheckAsync but just returns the license state.

ResetState

The license status is cached internally. Only after a certain period a live check against the server is made. With this method, the cached status is reset and immediately checked again live.

LicenseRequired filter attribute

You can decorate a whole controller class or just a single action method with the LicenseRequiredAttribute. This attribute calls LicenseChecker.CheckAsync() internally right before your action is processed, giving it the opportunity to block the action. If the license isn't valid, the LicenseRequired view will be rendered by default, which you can override by setting the property ViewName on the attribute.

Alternatively, you could just display a notification for which you can use the properties NotifyOnly, NotificationMessage and NotificationMessageType. AJAX requests will be recognized automatically, and a suitable response will be generated according to the negotiated content type (either JSON or HTML).

If you want to block certain methods or even your entire module when it is in demo mode, you need to set the property BlockDemo to True. Otherwise everything will be accessible in demo mode, as the value of BlockDemo is False by default. All properties of LicenseRequiredAttribute:

Property
Description

ModuleSystemName

Gets or sets the module system name.

LayoutName

Gets or sets the name of the layout view for the License Required message. ~/Views/Shared/Layouts/_Layout.cshtml by default.

ViewName

Gets or sets the name of the view for the License Required message. LicenseRequired by default.

Result

Gets or sets the action result if the module is in an unlicensed state. Possible values are:

  • Block: return a result with a license required warning.

  • NotFound: return a not found result.

  • Empty: return an empty result.

BlockDemo

A value indicating whether to block the request if the license is in demo mode.

NotifyOnly

A value indicating whether to only output a notification message and not to replace the action result.

NotificationMessage

Gets or sets the message to output if the module is in an unlicensed state.

NotificationMessageType

Gets or sets the type of the notification message. Possible values are:

  • error (Default)

  • success

  • info

  • warning

Usage scenario

Imagine you have developed a module to communicate with an ERP system. Furthermore, your module transmits data to a web service whenever an order is placed in your shop and consumes another web service to keep your product data up-to-date. If you decide to allow the product data to be updated completely in the demo mode of your plugin, it may be sufficient for the plugin user to import the product data only once. Therefore, you should interrupt the routine that's responsible for updating product data after a certain number of products have been updated. To do so, you would use the CheckStateAsync() method, which checks whether the state is Demo and stops the routine accordingly (see code example 1). This way, the user can see a demonstration of the actual function without losing the motivation to purchase the full version.

Order events should, of course, be processed and transmitted to the ERP system completely for demonstration purposes, as it's way too difficult to keep track of the number of processed orders. However, when the demonstration period is over, no more orders should be processed. Therefore, you would use the CheckStateAsync() method to check whether the state is Unlicensed and to stop the event accordingly (see code example 2).

Code example 1
private async Task ProcessProducts()
{
    var state = await LicenseChecker.CheckStateAsync("MyCompany.MyModule");
    // ...
    if (state == LicensingState.Demo)
    {
        // Leave after 5 products if module is in demo mode.
        products = products.Take(5);
    }
    
    foreach (var product in products)
    {
        await UpdateProduct(product);
    }
    // ...
Code example 2
public class Events : IConsumer
{
    public async Task HandleEventAsync(OrderPlacedEvent message)
    {
        var state = await LicenseChecker.CheckStateAsync("MyCompany.MyModule");
        if (state == LicensingState.Unlicensed)
            return;
        // ...

Examples and special case

In background tasks there is no HTTP request object, so a license is checked against the current store URL. But for an export provider this is a bit inaccurate. The provider will want to check the license against the URL of the store whose data is being exported. It can use the ExportExecuteContext to get the URL.

protected override async Task ExportAsync(ExportExecuteContext context,
    CancellationToken cancelToken)
{
    var license = await LicenseChecker.CheckAsync("MyCompany.MyModule",
        (string)context.Store.Url);

    if (license.State == LicensingState.Unlicensed)
    {
        context.Log.Error(HtmlUtility.ConvertHtmlToPlainText(
            license.ToString(), true, true));
        context.Abort = DataExchangeAbortion.Hard;
        return;
    }    
    // TODO: export...
}

Additionally, a payment provider can hide its payment methods at checkout if there is no active license. To enable this, you must override the PaymentMethodBase.IsActive property.

public override bool IsActive
{
    get
    {
        try
        {
            var state = LicenseChecker.CheckState("MyCompany.MyModule");
            return (state != LicensingState.Unlicensed);
        }
        catch (Exception ex)
        {
            // _logger is an instance of ILogger
            _logger.Error(ex);
        }
        return true;
    }
}

// Or when using the mentioned throttle check simply:
public override bool IsActive
    => Module.CheckLicense(false).Await();

Last updated

Was this helpful?