Making free extensions with paid features?

I’ve never worked with the extension license API and have a few questions that I haven’t found any answers for in the docs.

Is it possible to create one single extension that is completely free to download and install but has features that are locked unless the user has paid for a license?

I have some plans for a new extension and additional features for an existing extension that I think would benefit a lot from this. For most users, especially hobby users, the free version would be juust fine but for hardcore users such as many professionals it should really be worth the prize to upgrade. Also it has the benefit that the user can check so everything works as expected and that it really is the plugin nthey need before purchasing it.

Is this currently supported? Is it possible to have both a Buy button and a Download button for the same extension in the extension Warehouse? Are there any built in messages in Sketchup telling the user the license is missing even if they aren’ supposed to have one or that there is infinite time left on a trial period that really isn’t a trial period?

@Bryceosaurus? @thomthom?

Hi Christina

It’s not possible to have in-extension purchasing of features. Right now extension licenses are installed when the extension is installed or upgraded. So it would be difficult to support unlocking features as users are working in an extension. That said, It would be a great business model though. I’m sure many developers would take advantage if we had this capability.

@ChrisFullmer would be best to comment on the future direction of Ext Warehouse.

2 Likes

Thanks for the reply!

Can I for now develop just one extension but publish it twice with the only difference being a constant telling whether it’s the free or pro version (all license handling depends on the constant)? This would mean the two extensions share the same namespace and directory name. I don’t know if there is any policy forbidding this. It would make development a lot easier if there is just one code base to maintain.

I think each edition will need it’s own extension title (SketchupExtension#name) and ID (SketchupExtension#id) and therefor their ownextension_info.txt” file, which means their own extension registrar script, and their own extension sub-folder.

Not really necessary, but it might be possible, if the Pro edition loads after the Free edition, it could override the Free objects (classes, modules and methods,) provided that the Free objects have not yet been frozen.

Constants in Ruby are not yet “constant” and could be changed at runtime unless the last thing you do, and the end of your code is to freeze your extension sub-module for the Pro edition.

IMO, including “Pro” feature code along with a “Free/Hobby” edition is asking for a hack.

Yes it would, and Ruby is designed to help you do this using either Inheritance or Composition.

So firstly I’d suggest using composition (mixin modules), see:
Wikipedia: Composition over inheritance

An example:

Project folder structure

ene_widget_project
  |  # This would be the "Plugins" folder level.
  |  # In this folder would be each edition's 
  |  # SketchupExtension registrar script, ie:
  |  "ene_widget_hobby.rb"
  |  "ene_widget_pro.rb"
  |
  +-- ene_widget_lib
  |   # this contains the "core" subfolder for each edition,
  |   # and the common 'loader' script.
  |   # It will be copied into each edition's extension folder,
  |   # when building the rbz.
  |
  +-- ene_widget_hobby
  |   # edition specific extension sub-folder and hobby code
  |
  +-- ene_widget_pro
      # edition specific extension sub-folder and pro code

File:ene_widget_lib/core/core_module_functions_mixin.rb”:
(Will be copied into each edition’s extension folder.)

module Eneroth
  module WidgetExtensions
    module Lib
      module CoreModuleFunctionsMixin
        # Define core module functions common
        # to both Free and Pro editions in here,
        # and extend each plugin sub-module.
      end
    end
  end
end

File:ene_widget_lib/core/core_widget_class_mixin.rb”:
(Will be copied into each edition’s extension folder.)

module Eneroth
  module WidgetExtensions
    module Lib
      module CoreWidgetClassMixin
        # Define core class functions common to
        # both Free and Pro editions in here, and
        # include in each plugin's Widget class.
      end
    end
  end
end

File:ene_widget_lib/widget_loader.rb”:
(Will be copied into each edition’s extension folder.)

# This loader would be called by the edition's
# specific extension instance load() method.
module Eneroth
  module WidgetExtensions

    Dir::chdir(File.dirname(File.realpath(__FILE__))) {
      Dir::chdir('core') {
        Dir["*.{rbe,rbs,rb}"].each do |file|
          # Load all the core library files in alpha order:
          SketchUp::require File.basename(file,".*")
        end
      }
      Dir["*.{rbe,rbs,rb}"].each do |file|
        # Assumes 1st alpha-listed file should be loaded 1st:
        SketchUp::require File.basename(file,".*")
      end
    }

  end
end

File:ene_widget_hobby/widget_core.rb”:

module Eneroth
  module WidgetExtensions
    module WidgetHobby

      Lib = Module::nesting[1]::Lib
      extend Lib::CoreModuleFunctionsMixin

      class Widget
        include Lib::CoreWidgetClassMixin
        # add any Hobby edition specific code
      end

      # add any Hobby edition specific code

    end
  end
end

File:ene_widget_pro/widget_core.rb”:

module Eneroth
  module WidgetExtensions
    module WidgetPro

      Lib = Module::nesting[1]::Lib
      extend Lib::CoreModuleFunctionsMixin

      class Widget
        include Lib::CoreWidgetClassMixin
        # add any Pro edition specific code,
        # or override Core inherited methods
        # with only Pro functionality.
      end

      # add any Pro edition specific code

    end
  end
end

Now I only show a couple of library mixin modules (one for extending to create singleton module methods, and one for including to create instance methods,) … but you can make as many mixin modules as you need to organize and make maintenance of your project easier.

Oh, one thing I forgot to mention was, that during development, your edition extension loader script might need to be fake loader that points at the real one in the lib folder:

require('../ene_widget_lib/widget_loader.rb')

Otherwise, make two copies at the start and change the “core” chdir path to:

Dir::chdir('../ene_widget_lib/core') {

This is only during development.

Or, if you are more comfortable with symbolic links, you can create two “core” symlinks in the edition folders that point at the common lib folder. Just do not copy the symlinks into the RBZ, intead copy the “core” subfolder from the lib folder.

Hi Dan

Thanks for taking the time to write this answer. It is absolutely possible to handle it like this but it requires quite a bit of bolierplate code that makes the code harder to read for other devs or for the same dev if they take up the project again years later.

I think it would be easier to just run a macro to replaces the module name in all files at once before publishing in case the Extension Warehouse has a policy against several extensions using the same namespace. However I don’t know if there is such a policy. It wouldn’t make any sense to have both the free and paid edition installed at the same time so maybe they could both use the same module and directory name despite technically being hosted as if they were two different extensions. One potential issue would be if the extension warehouse allows for 2 “separate” extensions to have the same name for their rbz file.

There isn’t any such policy - at least not in terms of a developer reusing its own namespace. But you might run into users installing both versions - you might want to make some check against that. If there is a possibility it will happen - then most likely it will.

A build script doesn’t sound too bad to me though. I often have a packaging script that takes care of prepping for release.

1 Like

Back to the questions in the first post, what actually happens when a user tries to run an extension without a license? Is there an error message built in to Sketchup telling them the license is missing or is it up to the plugin dev to create such a message themselves if ExtensionLicense#licensed? returns false?

When SketchUp starts up it will display a dialog with a list of extensions that needs a license.

And there is no way to exclude an extension from that list but still check if it is licensed?

Btw, is there a screenshot anywhere what that message looks like?

It looks something like this:

There is a check during startup that will display failures of all licenses that has been checked at that point. If you don’t do a license check when your extension load, but move that check to the menu command invocation you might avoid that dialog.

1 Like

I can’t make much sense of this.

I’ve been experimenting a bit but I can’t get that window to show up. I’m running this on the plugin load inside my own module:

LICENCE = Sketchup::Licensing.get_extension_license("<censured extension unique identifier>")
LICENCE.licensed?

No message shows up but I can access the LICENCE constant in my own namespace and run the licensed? and state methods on it with sensible return values.

When I run “::LICENCE.error_description” “No license for product (-1)” is returned. Does the message fail to show up because SU can’t find the extension object related to the failed license check and if so, how do I link the license check to the extension that invokes it?

There really isn’t much documentation of the licensing API at all.

On a different note, I still haven’t signed up to be a vendor. Whom should I contact to do that?

I just got the message to show up by manually crafting an extension_info.txt file in the extension’s directory. Apparently this is the link. So you don’t need to answer that.

2 Likes

Fill out the form linked from “Apply to be a Developer”:
https://help.sketchup.com/en/extension-warehouse/developing-extensions

Well, because there really isn’t much to the licensing API at all.

There is an example:

https://github.com/SketchUp/sketchup-ruby-api-tutorials/tree/main/examples/99_license

There is some information here:


Let’s talk about that example on that tutorial page.

You should be able to see that someone could just type:

MyModule::MyLicensedRubyExtension::execute

… into the Ruby console, and bypass the license check.

Making a licensed extension’s command methods public module methods may not be a good idea.


On the class page for Sketchup::Licensing::ExtensionLicense, the Introduction tries to advise us not to keep persistent references to the class’ instances around.

If you are really concerned about tampering I’d recommend you make a Ruby C Extension perform the core logic of your extension - and make use of the C API function. That way people would have to decompile/crack your binary file to be able to bypass the license code.

Thanks. That question wasn’t there the previous time I filled in the form.

Thanks again.I forgot to check under Tutorials. It would make sense to include the example the documentation for the relevant classes, or at least link to it from there.

Regarding exposing the methods publicly I’m actually thinking about making a public API for my plugin so people can call it from there own code. Only certain features would be considered pro features and for those I’ll probably check the license inside the methods and raise an error if the license is missing.

I’m not hugely anxiously concerned but it would be good not to have any obvious security holes. I think the ruby encryption for SU 2016+ should be enough.

1 Like

Coupled with freezing objects (your module and it’s classes, etc.)

1 Like

Yup, it was added after the EW became purchase capable (along with the release of v 2015.)