Key Panel

C++ Implementation

Drop-in license validation for Windows apps. Copy the SDK files into your project, plug in your App ID and App Token from the dashboard, and call panel.license(key) at startup.

Your panel URLhttps://www.keypanel.cc

Quick start

Add keypanel.hpp and keypanel.cpp to your project (sources below), then:

main.cpp
#include "keypanel.hpp"

KeyPanel::api panel(
  "https://www.keypanel.cc",
  "your-app-id",      // Dashboard → Application → App ID
  "your-app-token"    // Dashboard → Application → App Token
);
panel.set_client_version("1.0.0");

if (!panel.license("LIC-XXXXXX-XXXXXX-XXXXXX")) {
  // panel.response.message has the error
  // update_required → panel.response.update_url
  return 1;
}

// panel.response.expires_at — ISO timestamp when set
// Your protected app logic starts here

API

MethodDescription
license(key)Validate on this device; auto-activate if not yet bound
activate(key)Bind license to this device (HWID)
validate(key)Check license is still valid on this device
set_client_version(ver)Send build version on every request (e.g. "3.1")
get_device_id()Windows MachineGuid used as HWID
set_device_id(id)Override HWID for testing

After each call, read panel.response for success, message, expires_at, activated, already_active, and on force-update: reason, min_client_version, update_url.

Credentials

DashboardSDK argument
Deploy URLbase_url
App IDapp_id
App Tokenapp_token

Integration steps

  1. Copy keypanel.hpp and keypanel.cpp into your project.
  2. Add keypanel.cpp to your build.
  3. WinHTTP is linked automatically via #pragma comment in the source.
  4. Obfuscate your app token in release builds.

Flow

App launch
   ↓
panel.license(user_key)
   ↓
POST /api/client/validate  → valid? → run app
   ↓ (device not activated)
POST /api/client/activate → bind HWID, start subscription clock

Users claim keys and manage accounts in the launcher. Your client only needs activate + validate — the SDK handles both via license().

SDK source

include/keypanel.hpp
#pragma once

#include <string>

namespace KeyPanel {

struct Response {
  bool success = false;
  std::string message;
  std::string expires_at;
  bool already_active = false;
  bool activated = false;
  std::string reason;
  std::string min_client_version;
  std::string latest_client_version;
  std::string update_url;
};

// Reads Windows MachineGuid from the registry (HWID). Falls back to computer name.
std::string get_device_id();

class api {
public:
  api(std::string base_url, std::string app_id, std::string app_token);

  void set_device_id(std::string device_id);
  const std::string& device_id() const;

  // Optional build version sent on every activate/validate (e.g. "3.1" or "v3.1.0").
  void set_client_version(std::string version);
  const std::string& client_version() const;

  // Validate on this device; activates automatically if not yet bound.
  bool license(const std::string& license_key);

  bool activate(const std::string& license_key);
  bool validate(const std::string& license_key);

  Response response;

private:
  std::string base_url_;
  std::string app_id_;
  std::string app_token_;
  std::string device_id_;
  std::string client_version_;

  void reset_response();
  void fill_version_fields(const std::string& raw);
  bool post_client(const wchar_t* path, const std::string& license_key, std::string& raw);
};

}  // namespace KeyPanel
src/keypanel.cpp
#include "keypanel.hpp"

#include <Windows.h>
#include <winhttp.h>

#include <cctype>
#include <sstream>
#include <vector>

#pragma comment(lib, "winhttp.lib")

namespace KeyPanel {
namespace {

std::string json_escape(const std::string& value) {
  std::string out;
  out.reserve(value.size() + 8);
  for (char c : value) {
    switch (c) {
      case '\\': out += "\\\\"; break;
      case '"': out += "\\\""; break;
      case '\n': out += "\\n"; break;
      case '\r': out += "\\r"; break;
      case '\t': out += "\\t"; break;
      default: out += c; break;
    }
  }
  return out;
}

std::string extract_json_string(const std::string& json, const std::string& key) {
  const std::string needle = "\"" + key + "\"";
  size_t pos = json.find(needle);
  if (pos == std::string::npos) return "";
  pos = json.find(':', pos + needle.size());
  if (pos == std::string::npos) return "";
  pos = json.find('"', pos);
  if (pos == std::string::npos) return "";
  ++pos;

  std::string out;
  while (pos < json.size()) {
    char c = json[pos++];
    if (c == '"') break;
    if (c == '\\' && pos < json.size()) {
      char esc = json[pos++];
      switch (esc) {
        case '"': out += '"'; break;
        case '\\': out += '\\'; break;
        case 'n': out += '\n'; break;
        case 'r': out += '\r'; break;
        case 't': out += '\t'; break;
        default: out += esc; break;
      }
    } else {
      out += c;
    }
  }
  return out;
}

bool json_has_true(const std::string& json, const std::string& key) {
  const std::string needle = "\"" + key + "\":true";
  return json.find(needle) != std::string::npos;
}

bool parse_url(
  const std::string& base_url,
  std::wstring& host,
  bool& https,
  INTERNET_PORT& port,
  std::wstring& base_path
) {
  std::string url = base_url;
  https = false;
  port = 80;
  if (url.rfind("https://", 0) == 0) {
    https = true;
    port = 443;
    url = url.substr(8);
  } else if (url.rfind("http://", 0) == 0) {
    url = url.substr(7);
  }

  const size_t slash = url.find('/');
  const std::string host_port = slash == std::string::npos ? url : url.substr(0, slash);
  base_path = slash == std::string::npos ? L"" : std::wstring(url.begin() + static_cast<ptrdiff_t>(slash), url.end());

  const size_t colon = host_port.find(':');
  const std::string host_str = colon == std::string::npos ? host_port : host_port.substr(0, colon);
  if (colon != std::string::npos) {
    port = static_cast<INTERNET_PORT>(std::stoi(host_port.substr(colon + 1)));
  }
  host = std::wstring(host_str.begin(), host_str.end());
  return !host.empty();
}

bool http_post_json(
  const std::wstring& host,
  bool https,
  INTERNET_PORT port,
  const std::wstring& path,
  const std::string& body,
  std::string& response,
  std::string& error
) {
  HINTERNET session = WinHttpOpen(
    L"KeyPanelSDK/1.0",
    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
    WINHTTP_NO_PROXY_NAME,
    WINHTTP_NO_PROXY_BYPASS,
    0
  );
  if (!session) {
    error = "WinHttpOpen failed.";
    return false;
  }

  HINTERNET connect = WinHttpConnect(session, host.c_str(), port, 0);
  if (!connect) {
    WinHttpCloseHandle(session);
    error = "WinHttpConnect failed.";
    return false;
  }

  const DWORD flags = https ? WINHTTP_FLAG_SECURE : 0;
  HINTERNET request = WinHttpOpenRequest(
    connect,
    L"POST",
    path.c_str(),
    nullptr,
    WINHTTP_NO_REFERER,
    WINHTTP_DEFAULT_ACCEPT_TYPES,
    flags
  );
  if (!request) {
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    error = "WinHttpOpenRequest failed.";
    return false;
  }

  const wchar_t* headers = L"Content-Type: application/json\r\nAccept: application/json\r\n";
  BOOL ok = WinHttpSendRequest(
    request,
    headers,
    static_cast<DWORD>(-1),
    const_cast<LPVOID>(static_cast<LPCVOID>(body.data())),
    static_cast<DWORD>(body.size()),
    static_cast<DWORD>(body.size()),
    0
  );
  if (!ok) {
    WinHttpCloseHandle(request);
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    error = "WinHttpSendRequest failed.";
    return false;
  }

  ok = WinHttpReceiveResponse(request, nullptr);
  if (!ok) {
    WinHttpCloseHandle(request);
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    error = "WinHttpReceiveResponse failed.";
    return false;
  }

  response.clear();
  DWORD available = 0;
  do {
    if (!WinHttpQueryDataAvailable(request, &available)) break;
    if (available == 0) break;
    std::vector<char> chunk(available);
    DWORD read = 0;
    if (!WinHttpReadData(request, chunk.data(), available, &read)) break;
    response.append(chunk.data(), read);
  } while (available > 0);

  WinHttpCloseHandle(request);
  WinHttpCloseHandle(connect);
  WinHttpCloseHandle(session);
  return true;
}

std::string reason_to_message(const std::string& reason) {
  if (reason == "not_found") return "License not found.";
  if (reason == "expired") return "License expired.";
  if (reason == "revoked") return "License revoked.";
  if (reason == "device_not_activated") return "Device not activated.";
  if (reason == "update_required") {
    return "Update required. Please install the latest version.";
  }
  if (reason == "organization_disabled") return "Organization disabled.";
  if (reason.empty()) return "License is not valid on this device.";
  return "License invalid: " + reason + ".";
}

}  // namespace

std::string get_device_id() {
  HKEY key = nullptr;
  if (RegOpenKeyExA(
        HKEY_LOCAL_MACHINE,
        "SOFTWARE\\Microsoft\\Cryptography",
        0,
        KEY_READ | KEY_WOW64_64KEY,
        &key
      ) == ERROR_SUCCESS) {
    char guid[128]{};
    DWORD size = sizeof(guid);
    if (RegQueryValueExA(
          key,
          "MachineGuid",
          nullptr,
          nullptr,
          reinterpret_cast<LPBYTE>(guid),
          &size
        ) == ERROR_SUCCESS) {
      RegCloseKey(key);
      return std::string(guid);
    }
    RegCloseKey(key);
  }

  char name[MAX_COMPUTERNAME_LENGTH + 1]{};
  DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
  if (GetComputerNameA(name, &len)) {
    return std::string(name);
  }
  return "unknown-device";
}

api::api(std::string base_url, std::string app_id, std::string app_token)
  : base_url_(std::move(base_url)),
    app_id_(std::move(app_id)),
    app_token_(std::move(app_token)),
    device_id_(get_device_id()) {}

void api::set_device_id(std::string device_id) {
  device_id_ = std::move(device_id);
}

const std::string& api::device_id() const {
  return device_id_;
}

void api::set_client_version(std::string version) {
  client_version_ = std::move(version);
}

const std::string& api::client_version() const {
  return client_version_;
}

void api::reset_response() {
  response = Response{};
}

void api::fill_version_fields(const std::string& raw) {
  response.reason = extract_json_string(raw, "reason");
  response.min_client_version = extract_json_string(raw, "minClientVersion");
  response.latest_client_version = extract_json_string(raw, "latestClientVersion");
  response.update_url = extract_json_string(raw, "updateUrl");
}

bool api::post_client(const wchar_t* path, const std::string& license_key, std::string& raw) {
  std::ostringstream body;
  body << "{\"appId\":\"" << json_escape(app_id_) << "\","
       << "\"appToken\":\"" << json_escape(app_token_) << "\","
       << "\"licenseKey\":\"" << json_escape(license_key) << "\","
       << "\"deviceId\":\"" << json_escape(device_id_) << "\"";
  if (!client_version_.empty()) {
    body << ",\"clientVersion\":\"" << json_escape(client_version_) << "\"";
  }
  body << "}";

  std::wstring host;
  std::wstring base_path;
  bool https = false;
  INTERNET_PORT port = 80;
  if (!parse_url(base_url_, host, https, port, base_path)) {
    response.message = "Invalid api base URL.";
    return false;
  }

  const std::wstring full_path = base_path + path;
  return http_post_json(host, https, port, full_path, body.str(), raw, response.message);
}

bool api::activate(const std::string& license_key) {
  reset_response();

  std::string raw;
  if (!post_client(L"/api/client/activate", license_key, raw)) {
    return false;
  }

  fill_version_fields(raw);

  if (!json_has_true(raw, "ok")) {
    if (response.reason == "update_required") {
      response.message = reason_to_message(response.reason);
      const std::string server_msg = extract_json_string(raw, "message");
      if (!server_msg.empty()) response.message = server_msg;
      return false;
    }
    response.message = extract_json_string(raw, "error");
    if (response.message.empty()) response.message = "Activation failed.";
    return false;
  }

  response.success = true;
  response.already_active = json_has_true(raw, "alreadyActive");
  response.activated = json_has_true(raw, "activated");
  response.expires_at = extract_json_string(raw, "expiresAt");
  if (response.already_active) {
    response.message = "Already active on this device.";
  } else if (response.activated) {
    response.message = "Device activated.";
  } else {
    response.message = "Activation successful.";
  }
  return true;
}

bool api::validate(const std::string& license_key) {
  reset_response();

  std::string raw;
  if (!post_client(L"/api/client/validate", license_key, raw)) {
    return false;
  }

  fill_version_fields(raw);

  if (!json_has_true(raw, "ok")) {
    response.message = extract_json_string(raw, "error");
    if (response.message.empty()) response.message = "Validation request failed.";
    return false;
  }

  if (!json_has_true(raw, "valid")) {
    if (response.reason.empty()) {
      response.reason = extract_json_string(raw, "reason");
    }
    if (response.reason == "update_required") {
      const std::string server_msg = extract_json_string(raw, "message");
      response.message = server_msg.empty() ? reason_to_message(response.reason) : server_msg;
    } else {
      response.message = reason_to_message(response.reason);
    }
    return false;
  }

  response.success = true;
  response.expires_at = extract_json_string(raw, "expiresAt");
  response.message = "License valid.";
  return true;
}

bool api::license(const std::string& license_key) {
  if (validate(license_key)) {
    return true;
  }

  if (response.reason == "update_required") {
    return false;
  }

  const std::string last_message = response.message;
  if (last_message.find("Device not activated") == std::string::npos &&
      last_message.find("device_not_activated") == std::string::npos) {
    return false;
  }

  return activate(license_key);
}

}  // namespace KeyPanel

Example project

example/main.cpp
#include "keypanel.hpp"

#include <iostream>
#include <string>

int main() {
  // Credentials from Key Panel dashboard → your Application.
  // Prefer downloading the per-app SDK zip (credentials prefilled).
  const std::string base_url = "https://keypanel.cc";
  const std::string app_id = "your-app-id";
  const std::string app_token = "your-app-token";

  KeyPanel::api panel(base_url, app_id, app_token);
  panel.set_client_version("1.0.0");  // match your build; required if the app sets a minimum

  std::cout << "Device ID: " << panel.device_id() << "\n";
  std::cout << "Enter license key: ";

  std::string license_key;
  std::getline(std::cin, license_key);

  if (!panel.license(license_key)) {
    std::cerr << "Failed: " << panel.response.message << "\n";
    if (panel.response.reason == "update_required") {
      if (!panel.response.update_url.empty()) {
        std::cerr << "Update: " << panel.response.update_url << "\n";
      }
    }
    return 1;
  }

  std::cout << "OK: " << panel.response.message << "\n";
  if (!panel.response.expires_at.empty()) {
    std::cout << "Expires: " << panel.response.expires_at << "\n";
  }

  // Your protected app logic starts here.
  return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(keypanel_sdk LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(keypanel STATIC
  src/keypanel.cpp
)
target_include_directories(keypanel PUBLIC include)

add_executable(keypanel_example example/main.cpp)
target_link_libraries(keypanel_example PRIVATE keypanel)

Download SDK

Zip archive with everything you need to get started.

  • include/keypanel.hpp
  • src/keypanel.cpp
  • CMakeLists.txt
  • example/main.cpp
Download keypanel-cpp-sdk.zip