loader

How to integrate purchase-code licensing and product updates

Developers ยท Updated 17 Jul 2026

License API overview

Authors can protect their applications by validating a customer purchase code against the DigiMarket API. Each request must use the author's API key from the author account. The API binds the license to the installation identity built from server IP, domain, site URL or an explicit machine ID.

Endpoints

  • POST /api/license-manager/purchase/verify verifies, activates or deactivates a purchase code.
  • POST /api/license-manager/updates/check checks if an approved patch or full product package is available.
  • GET|POST /api/license-manager/updates/{release}/download downloads the approved update package only when the license is valid for that server.

PHP activation

$payload = [
    'action' => 'activate',
    'purchase_code' => $purchaseCode,
    'product_token' => 'your-product-slug',
    'server_ip' => $_SERVER['SERVER_ADDR'] ?? '',
    'server_host' => $_SERVER['HTTP_HOST'] ?? '',
    'site_url' => 'https://' . ($_SERVER['HTTP_HOST'] ?? ''),
    'platform' => 'php',
    'app_version' => '1.0.0',
];

$ch = curl_init('https://your-marketplace.com/api/license-manager/purchase/verify');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_AUTHOR_API_KEY',
        'Accept: application/json',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

JavaScript server-side example

const response = await fetch("https://your-marketplace.com/api/license-manager/purchase/verify", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_AUTHOR_API_KEY",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    action: "activate",
    purchase_code: purchaseCode,
    product_token: "your-product-slug",
    server_ip: request.ip,
    server_host: request.hostname,
    site_url: `https://${request.hostname}`,
    platform: "node",
    app_version: "1.0.0"
  })
});
const data = await response.json();

Python example

import requests
data = requests.post(
    "https://your-marketplace.com/api/license-manager/purchase/verify",
    json={
        "action": "activate",
        "purchase_code": purchase_code,
        "product_token": "your-product-slug",
        "server_ip": server_ip,
        "server_host": server_host,
        "site_url": site_url,
        "platform": "python",
        "app_version": "1.0.0",
    },
    headers={"Authorization": "Bearer YOUR_AUTHOR_API_KEY", "Accept": "application/json"},
    timeout=20,
).json()

Windows C++ desktop installer example

#include <windows.h>
#include <winhttp.h>
#include <string>

// Link with winhttp.lib.
// This minimal example shows the request shape. Production apps should add
// robust JSON parsing, TLS error handling and retry limits.
std::wstring apiHost = L"your-marketplace.com";
std::wstring apiPath = L"/api/license-manager/purchase/verify";
std::string body =
    "{\"action\":\"activate\","
    "\"purchase_code\":\"CUSTOMER-PURCHASE-CODE\","
    "\"product_token\":\"your-product-slug\","
    "\"machine_id\":\"WINDOWS-MACHINE-FINGERPRINT\","
    "\"platform\":\"windows-cpp\","
    "\"app_version\":\"1.0.0\"}";

HINTERNET session = WinHttpOpen(L"YourApp/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET connect = WinHttpConnect(session, apiHost.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
HINTERNET request = WinHttpOpenRequest(connect, L"POST", apiPath.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);

std::wstring headers =
    L"Authorization: Bearer YOUR_AUTHOR_API_KEY\r\n"
    L"Accept: application/json\r\n"
    L"Content-Type: application/json\r\n";

WinHttpSendRequest(request, headers.c_str(), (DWORD)-1L, (LPVOID)body.c_str(), (DWORD)body.size(), (DWORD)body.size(), 0);
WinHttpReceiveResponse(request, NULL);
// Read the response body and continue only when the JSON contains valid=true.

Linux shell / installable app example

curl -X POST "https://your-marketplace.com/api/license-manager/purchase/verify" \
  -H "Authorization: Bearer YOUR_AUTHOR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "action":"activate",
    "purchase_code":"CUSTOMER-PURCHASE-CODE",
    "product_token":"your-product-slug",
    "machine_id":"'$(cat /etc/machine-id 2>/dev/null)'",
    "server_ip":"'$(hostname -I | awk "{print \$1}")'",
    "server_host":"'$(hostname -f)'",
    "platform":"linux",
    "app_version":"1.0.0"
  }'

C# / .NET installer example

using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_AUTHOR_API_KEY");
client.DefaultRequestHeaders.Add("Accept", "application/json");

var response = await client.PostAsJsonAsync(
    "https://your-marketplace.com/api/license-manager/purchase/verify",
    new {
        action = "activate",
        purchase_code = purchaseCode,
        product_token = "your-product-slug",
        machine_id = Environment.MachineName,
        platform = "dotnet",
        app_version = "1.0.0"
    }
);
var json = await response.Content.ReadAsStringAsync();

Go command-line application example

payload := strings.NewReader(`{
  "action":"activate",
  "purchase_code":"CUSTOMER-PURCHASE-CODE",
  "product_token":"your-product-slug",
  "machine_id":"SERVER-FINGERPRINT",
  "platform":"go",
  "app_version":"1.0.0"
}`)

req, _ := http.NewRequest("POST", "https://your-marketplace.com/api/license-manager/purchase/verify", payload)
req.Header.Set("Authorization", "Bearer YOUR_AUTHOR_API_KEY")
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)

Update check

The update API returns update_mode as patch when a patch package exists, or full when the customer must download and reinstall the complete product package. Authors always upload the complete product package for every update. A patch ZIP is optional and is served only when it exists and the release has been approved by an admin or reviewer.

{
  "purchase_code": "CUSTOMER-PURCHASE-CODE",
  "product_token": "your-product-slug",
  "current_version": "1.0.0",
  "server_ip": "203.0.113.10",
  "server_host": "example.com",
  "site_url": "https://example.com"
}

Update response shape

{
  "valid": true,
  "update_available": true,
  "update_mode": "patch",
  "patch_available": true,
  "full_required": false,
  "release": {
    "version": "1.2.0",
    "download_url": "https://your-marketplace.com/api/license-manager/updates/12/download?package=patch",
    "patch_package": {"file_name": "patch-1.2.0.zip"},
    "full_package": {"file_name": "product-1.2.0.zip"}
  }
}