EDD Software Licensing For Excel Add-In

I have been selling an Add-In for Microsoft Excel on another website for a number of years now. It has sold OK and recently has been selling quite a bit. I never really invested the time or effort to create any sort of licensing function with it, so pretty much whenever someone purchased it they were free to install it on as many computers as they wanted and I’d be none the wiser. I’m sure this resulted in quite a bit of lost revenue over the years, so I decided to do something about it.

If you have purchased any of my WordPress plugins, I use the Easy Digital Downloads Software Licensing extension to manage how many sites a plugin can be installed on. The way it works is a customer purchases the plugin or some other download file like an Excel Add-In, and they are provided with a license key that is valid for a period of time after the date of purchase. On the download file, there is a license key validation that takes place in order for the software to be used and updated.

Now originally the Software Licensing extension was designed for use with WordPress plugins but it has a complete API that allows interaction with other digital products including software licensing for Excel Add-In.

Software Licensing for Excel Add-In

Interacting with the Easy Digital Downloads Software Licensing for Excel Add-In presented a few challenges because as far as I could tell it wasn’t ever really attempted. At least I couldn’t find any documentation on it, and the Easy Digital Downloads support staff didn’t have any specific advice on how to go about setting it up. That’s not to say that their support isn’t top-notch because they certainly are! Excel Add-Ins just aren’t their focus. That’s why I’m writing this guide.

My goals for the licensing component of the add-in were:

  • Force the user to activate a valid license key that was not in use elsewhere in order to use the add-in.
  • Allow the user to deactivate a license in the event that they ever want to use the add-in on another computer.
  • Store license information in the add-in and check whether or not it is valid when Excel starts up.
  • Make the user experience as easy and intuitive as possible.

What you’ll need in order to follow along:

  • A self-hosted website running WordPress.
  • Install Easy Digital Downloads plugin on your website.
  • Install the Software Licensing extension for Easy Digital Downloads on your website.
  • A downloadable product in Easy Digital Downloads
  • Optionally install the Discounts Pro extension for Easy Digital Downloads on your website. I used this to offer discounts based on the number of licenses that were purchased. You could use the software licensing extension to do something similar, but I wanted to keep each license limited to one activation at a time. If you opt out of this step you may need to tweak your code a little to get the checks right.
  • A Windows version of Excel (this might work on Mac versions, but I haven’t tested it). I used Excel 2010 on Windows 10, although I believe this will work on any version of Excel after 2003.

The remainder of the tutorial assumes you have everything in the list above.

Software Licensing for Excel Add-In Step 1:

The Easy Digital Downloads Software Licensing documentation provides a few useful guides that help you connect to your shop to perform actions such as activate, deactivate, check, and get the version of your license. While you may be developing in other languages, all you’ll need for this example is the URL right at the top of their documentation.

http://YOURSITE.com/?edd_action={request type}&item_name=EDD+Product+Name&license=cc22c1ec86304b36883440e2e84cddff

If you notice on their documentation there is a field in the link that I did not include &url=. This is used to associate a license with website software like a WordPress plugin, but really has no purpose for desktop software so we can leave it off.

What we’ll need to do is create some code to check the license to see if it is valid or not, to activate the license, and to deactivate the license. In order to do this we will save some information in the add-in itself. Since an add-in is basically the same as any other workbook we can save data to a worksheet in the add-in without writing to any other active workbook. In my add-in I saved data to Sheet1 of the add-in in the following locations:

  • Cell A1: the license key entered by the end user.
  • Cell A2: the JSON object response from the website when it checks the license key’s status.
  • Cell A3: a formula to extract the license status from the JSON object in A2.
  • Cell A4: a formula to extract the number of activations left from the JSON object in A2. In my scenario, the only valid responses will be 1 for a license that is not active or 0 for a license that is active. You may have more if you choose to allow a license to be activated more than once.

To check the license:

'json response from Software Licensing in the following format
'{"success":true,"license":"valid","item_name":"Download Product Name","expires":"lifetime","payment_id":"54224","customer_name":"John Doe","customer_email":"john@sample.com","license_limit":1,"site_count":1,"activations_left":0}
 
Sub httpRequestCheck()
'use to check license once when workbook opens
 
    Dim oRequest As Object
    Const cUrl As String = "https://yoursite.com/?edd_action=check_license&item_name=Name of the Download Product&license="
 
    URL = cUrl & ThisWorkbook.Worksheets("Sheet1").Range("A1").Value
    Set oRequest = CreateObject("WinHttp.WinHttpRequest.5.1")
    oRequest.Open "GET", URL
    oRequest.Send
    ThisWorkbook.Worksheets("Sheet1").Range("A2:A4").ClearContents
    ThisWorkbook.Worksheets("Sheet1").Range("A2").Value = oRequest.ResponseText
    'The MID and FIND functions allow for searching through the json object for certain terms (license and activations_left) then return a certain number of characters after it.
    'MID will start at the first character of the word in FIND, offset a number of characters (10 for license), then return a number of characters after (8 for the word inactive, 5 for valid, etc.).
    '2x Double quotes are needed to enter formulas this way, so "license" wouldn't work but ""license"" will.
    ThisWorkbook.Worksheets("Sheet1").Range("A3").Formula = "=IF(MID($A$2,FIND(""license"",$A$2,1)+10,8)=""inactive"",""inactive"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,5)=""valid"",""valid"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,7)=""invalid"",""invalid"","""")))"
    ThisWorkbook.Worksheets("Sheet1").Range("A4").Formula = "=MID($A$2,FIND(""activations_left"",$A$2,1)+18,1)"
End Sub

To activate the license:

Sub httpRequestActivate()
    'Run the httpRequestCheck to see if the license has used up all of the activations it had available. 
    'If it has we add in_use to cell A5 so that we can avoid allowing the license to be activated on more computers than our license allows.
    'Note after the first activation, the license will return as valid before the end of the license expiration even if it has been deactivated so we need to check for remaining activations this way as well.
    httpRequestCheck
        With ThisWorkbook.Worksheets("Sheet1").Range("A1")
            If .Cells(4, 1).Value = 0 Then
                'Cells(4, 1).Value is the number of activations remaining. In my case, there will only be 1 or 0 but even if you allow more than one activation, 0 is where we should stop allowing it to be activated.
                'This indicates that the license is in use on as many computers as the license will allow. We will check for this later.
                .Cells(5, 1).Value = "in_use"
            Else
                Dim oRequest As Object
                Const cUrl As String = "https://yoursite.com/?edd_action=activate_license&item_name=Name of the Download Product&license="
 
                URL = cUrl & ThisWorkbook.Worksheets("Sheet1").Range("A1").Value
                Set oRequest = CreateObject("WinHttp.WinHttpRequest.5.1")
 
                oRequest.Open "GET", URL
                oRequest.Send
                'When activating for the first time we need to activate the license and then use it, so we're basically sending the request twice.
                'This step isn't necessary after the initial activation, so I suppose a check could be added to remove the redundancy.
                oRequest.Open "GET", URL
                oRequest.Send
 
                ThisWorkbook.Worksheets("Sheet1").Range("A2:A5").ClearContents
                ThisWorkbook.Worksheets("Sheet1").Range("A2").Value = oRequest.ResponseText
                ThisWorkbook.Worksheets("Sheet1").Range("A3").Formula = "=IF(MID($A$2,FIND(""license"",$A$2,1)+10,8)=""inactive"",""inactive"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,5)=""valid"",""valid"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,7)=""invalid"",""invalid"","""")))"
                ThisWorkbook.Worksheets("Sheet1").Range("A4").Formula = "=MID($A$2,FIND(""activations_left"",$A$2,1)+18,1)"
            End If
        End With
End Sub

To deactivate the license:

Sub httpRequestDeactivate()
'Use to deactivate license. Mostly the same as httpRequestActivate but without the check for if the max number of activations has been reached.
 
    Dim oRequest As Object
    Const cUrl As String = "https://yoursite.com/?edd_action=deactivate_license&item_name=Name of the Download Product&license="
 
    URL = cUrl & ThisWorkbook.Worksheets("Sheet1").Range("A1").Value
    Set oRequest = CreateObject("WinHttp.WinHttpRequest.5.1")
    oRequest.Open "GET", URL
    oRequest.Send
    ThisWorkbook.Worksheets("Sheet1").Range("A2:A4").ClearContents
    ThisWorkbook.Worksheets("Sheet1").Range("A2").Value = oRequest.ResponseText
    ThisWorkbook.Worksheets("Sheet1").Range("A3").Formula = "=IF(MID($A$2,FIND(""license"",$A$2,1)+10,8)=""inactive"",""inactive"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,5)=""valid"",""valid"",IF(MID($A$2,FIND(""license"",$A$2,1)+10,7)=""invalid"",""invalid"","""")))"
    ThisWorkbook.Worksheets("Sheet1").Range("A4").Formula = "=MID($A$2,FIND(""activations_left"",$A$2,1)+18,1)"
End Sub

Software Licensing for Excel Add-In Step 2:

Create a UserForm for your users to enter their license key. My UserForm consisted of a label instructing the user what they should do with the form, a text box for the license key to be entered into, two buttons (one to activate and another to deactivate the license), and a label with no caption that will be used to display whether or not a license was valid.

Software Licensing for Excel Add-In User Form

To set the Activate license button:

Private Sub LicenseKeyActivate_Click()
    'license.Value is what the user entered into the text box on the UserForm.
    If license.Value = "" Then
        MsgBox "Please enter a license key.", vbExclamation, "Input Data"
        Exit Sub
    Else
        Application.ScreenUpdating = False
 
        With ThisWorkbook.Worksheets("Sheet1").Range("A1")
            .Cells(1, 1).Value = license.Value
        End With
 
        ThisWorkbook.Save
        httpRequestActivate
        Unload Me
 
        Application.ScreenUpdating = True
 
        httpRequestCheck
 
        If ThisWorkbook.Worksheets("Sheet1").Cells(5, 1).Value = "in_use" Then
            MsgBox "This license key is already in use. Please use another key or deactivate it on the other computer where it is being used."
            'You should call a function to hide or disable your add-in here otherwise it can still be used until the user restarts Excel.
        Else
            With ThisWorkbook.Worksheets("Sheet1").Range("A1")
                If .Cells(3, 1).Value = "valid" And .Cells(4, 1).Value = 0 Then
                    MsgBox "Your license key has been activated."
                    'Call your add-in here otherwise the user will need to close and re-open Excel.
                Else
                    MsgBox "Your license key may not have been activated. Please close and restart Excel to re-check your license."
                End If
            End With
        End If
    End If
End Sub

To set the Deactivate license button:

Private Sub LicenseKeyDeactivate_Click()
    If license.Value = "" Then
        MsgBox "Please enter a license key.", vbExclamation, "Input Data"
        Exit Sub
    Else
 
        Application.ScreenUpdating = False
 
        With ThisWorkbook.Worksheets("Sheet1").Range("A1")
            .Cells(1, 1).Value = license.Value
        End With
 
        ThisWorkbook.Save
        httpRequestDeactivate
        Unload Me
 
        Application.ScreenUpdating = True
 
        httpRequestCheck
 
        With ThisWorkbook.Worksheets("Sheet1").Range("A1")
            If .Cells(4, 1).Value <> 0 Then
                MsgBox "Your license key has been deactivated. If you no longer intend to use this add-in on this computer please remove it from your list of Add-Ins to avoid license activation prompts at startup."
                'You should call a function to hide or disable your add-in here otherwise it can still be used until the user restarts Excel.
            Else
                MsgBox "Your license key may not have been deactivated. Please close and restart Excel to re-check your license."
                'You should call a function to hide or disable your add-in here otherwise it can still be used until the user restarts Excel.
            End If
        End With
    End If
End Sub

To grab the license data stored in the add-in:

Private Sub userform_initialize()
    With ThisWorkbook.Worksheets("Sheet1").Range("A1")
        license.Value = .Cells(1, 1).Value
        Label2.Caption = "Your license key is " & .Cells(3, 1).Value & " and has " & .Cells(4, 1).Value & " activations remaining."
    End With
End Sub

Software Licensing for Excel Add-In Step 3:

Finally, we need to check whether or not the license key has been activated and is valid when the add-in starts up. If it is we’ll show the add-in’s content and if not we’ll show the license activation UserForm we created earlier.

We need to tell the add-in how to show the UserForm we created. We can call this whenever it is needed.

Sub Activate_License()
    'Show the license key activation UserForm (mine is creatively named LicenseKey)
    LicenseKey.Show
End Sub

When the add-in starts up we’ll tell it to run the httpRequestCheck that we set up earlier. Then we will need to check whether or not the license is valid and has not exceeded the max number of activations.

Private Sub Workbook_Open()
    httpRequestCheck
    With ThisWorkbook.Worksheets("Sheet1").Range("A1")
        ' the And .Cells(4, 1) = 0 is probably redundant, and if you are allowing more than one activation per license key you should remove that part.
        If .Cells(3, 1).Value = "valid" And .Cells(4, 1) = 0 And .Cells(5, 1) <> "in_use" Then
            'call your add-in here
        Else
            'if the license is not valid, the number of activations remaining is greater than 0, and it is not in use elsewhere we should display the license activation UserForm.
            Activate_License
        End If
    End With
End Sub

Software Licensing for Excel Add-In Step 4:

This step is optional. Since I only display the license UserForm when the add-in is first opened and never again if there is a valid license in use, I wanted to provide a way for the user to display the form again so they could deactivate the license if they choose to do so. My add-in creates a custom toolbar, so I added an icon to the toolbar that calls Activate_License. If you recall that contains the code to show my UserForm: LicenseKey.Show.

Software Licensing for Excel Add-In Notes

This method is far from foolproof. If a customer really wanted to change this code around to disable the license checks they certainly could.

To prevent tampering with the code you could password protect your Excel Add-In code, but there are ways to crack those passwords. I won’t link to them here, but spend 5 minutes on Google searching for Excel password crackers and you’ll see what I mean.

If someone really wants to mess around and remove your license checks they probably can and there isn’t much you’re going to do about that. What this is designed to do is provide a way for the honest users of your hard work to validate that they purchased your code.

With that said, I have found that most of the time if someone is technically savvy enough to do all of that and cheap enough to not spring for the extra license they would likely create the add-in on their own. YMMV.

If this has helped you out, share a link to your software in the comments below. I’d love to check it out!

9 Comments

  1. SCOTT ANDERS on May 23, 2016 at 5:26 pm

    Thanks, Scott. I’ve been working on a very similar approach to an add-in I’ve developed. Since you implemented this, do you have any updates or additional advice?

    • Scott on May 24, 2016 at 8:53 am

      I haven’t had any updates since writing this article, although I have noticed since writing this that one downside does exist in the setup I described. The user needs to have an internet connection in order for the license to be verified at startup. I haven’t had a chance to update the license check to save the information for offline usage as well, though I’m sure it’s possible.

  2. Istvan on January 8, 2018 at 12:53 pm

    Very informative article. Thanks for sharing!

  3. ihsan sahab on April 23, 2020 at 7:44 am

    thanks very much, improved

  4. Roy on June 6, 2021 at 7:11 am

    You probably find an error using

    Private Sub userform_initialize()
    With ThisWorkbook.Worksheets(“Sheet1”).Range(“A1”)
    license.Value = .Cells(1, 1).Value
    Label2.Caption = “Your license key is ” & .Cells(3, 1).Value & ” and has ” & .Cells(4, 1).Value & ” activations remaining.”
    End With
    End Sub

    It should be

    Private Sub userform_initialize()
    With ThisWorkbook.Worksheets(“Sheet1”)
    license.Value = .Cells(1, 1).Value
    Label2.Caption = “Your license key is ” & .Cells(3, 1).Value & ” and has ” & .Cells(4, 1).Value & ” activations remaining.”
    End With
    End Sub

  5. Dakr on December 7, 2021 at 7:25 am

    Hi it’s been 4 years since this article was written. Any updates on how one might approach licensing Excel add ins? thanks.

    • Scott DeLuzio on December 7, 2021 at 9:11 am

      Honestly, I haven’t worked with Excel add-ins much since I first wrote this article. I would imagine that it would work much the same way as it had in the past. I don’t believe that anything introduced in either Excel or Easy Digital Downloads would prevent the methods described in this article from working. Have you attempted to implement it and had any problems?

  6. Danesvar on November 29, 2023 at 12:12 am

    Hello,
    I’m trying to license my add-in for sale without having to save the details into a sheet,
    Any advice on how I should edit my code below?
    Thank you

    Option Explicit

    ‘ Global variables to store the activation status, product ID, and API key
    Dim IsActivated As Boolean
    Dim ProductID As String
    Dim APIKey As String

    ‘ Set the product ID and API key constants
    Const YOUR_PRODUCT_ID As String = “YOUR_PRODUCT_ID”
    Const YOUR_API_KEY As String = “YOUR_API_KEY”

    Sub SetupAddIn()
    ‘ Set the product ID and API key directly in the code
    ProductID = YOUR_PRODUCT_ID
    APIKey = YOUR_API_KEY

    ‘ Save the entered product ID and API key securely
    SaveToConfigFile “ProductID”, ProductID
    SaveToConfigFile “APIKey”, APIKey
    End Sub

    Function CheckLicenseKeyActivation() As Boolean
    If Not IsActivated Then
    Dim licenseKey As String
    licenseKey = InputBox(“Enter your license key:”)

    If IsValidLicenseKey(licenseKey) Then
    IsActivated = True
    SaveActivationStatus IsActivated
    MsgBox “License activation successful. You can now use the add-in.”
    Else
    MsgBox “Invalid license key. Please contact support for assistance.”
    End If
    End If

    ‘ Return the activation status
    CheckLicenseKeyActivation = IsActivated
    End Function

    Function IsValidLicenseKey(key As String) As Boolean
    ‘ Use the stored ProductID and APIKey
    Dim url As String
    Dim response As String
    Dim json As Object

    ‘ Use the constants for the product ID and API key
    url = “https://your-licensing-server.com/verify?product_id=” & YOUR_PRODUCT_ID & “&license_key=” & key
    response = GetHTTPResponse(url, YOUR_API_KEY)

    On Error Resume Next
    Set json = CreateObject(“MSScriptControl.ScriptControl”)
    json.Language = “JScript”
    json.ExecuteStatement “var result = ” & response
    On Error GoTo 0

    ‘ Check if the “success” property exists and is true
    On Error Resume Next
    Dim successValue As Variant
    successValue = json(“success”)
    On Error GoTo 0

    If VarType(successValue) = vbBoolean Then
    ‘ “success” property exists and is a boolean
    IsValidLicenseKey = successValue
    Else
    ‘ “success” property does not exist or is not a boolean
    IsValidLicenseKey = False
    End If
    End Function

    Function GetHTTPResponse(url As String, api_key As String) As String
    Dim http As Object

    Set http = CreateObject(“MSXML2.XMLHTTP”)

    http.Open “GET”, url, False
    http.setRequestHeader “Authorization”, “Bearer ” & api_key
    http.send

    GetHTTPResponse = http.responseText
    End Function

    Sub SaveActivationStatus(activated As Boolean)
    ‘ Save activation status to a secure configuration file
    SaveToConfigFile “ActivationStatus”, CStr(activated)
    End Sub

    Function LoadActivationStatus() As Boolean
    ‘ Load activation status from the secure configuration file
    LoadActivationStatus = CBool(LoadFromConfigFile(“ActivationStatus”))
    End Function

    Sub SaveToConfigFile(key As String, value As String)
    Dim filePath As String
    ‘ Prompt the user to choose the file path
    filePath = Application.GetSaveAsFilename(“Save As”, FileFilter:=”Text Files (*.txt), *.txt”)

    If filePath “False” Then
    ‘ Save the key and value to the chosen file path
    ‘ You would need to implement the actual saving mechanism here
    ‘ For example, using File I/O or other suitable methods
    Else
    MsgBox “Saving canceled by the user.”, vbExclamation
    End If
    End Sub

    Function LoadFromConfigFile(key As String) As String
    ‘ Load the value from a secure storage mechanism
    ‘ For simplicity, you can use the Windows Registry or a more secure approach
    ‘ …

    ‘ For example, loading from Windows Registry:
    On Error Resume Next
    LoadFromConfigFile = VBA.Interaction.GetSetting(“YourCompanyName”, “YourAddInName”, key, “”)
    On Error GoTo 0
    End Function

    • Scott DeLuzio on November 29, 2023 at 9:40 am

      You’re asking for advice on how to edit the code, but I need to know what you’re trying to accomplish with the edit. Does the code work now, but you’re looking to optimize it? Does the code not work, and are you trying to figure out why? Something else?
      Honestly, it’s been years since I’ve worked with Excel add-ins, and I haven’t touched anything remotely close to this type of licensing in a long time. This post only exists to guide people to a solution to license their Excel add-ins, but I’m no expert in implementing it. The best way to figure out something like this is trial and error – that’s how I got the code introduced in this post.

Leave a Comment





This site uses Akismet to reduce spam. Learn how your comment data is processed.