Skip to content

SDK 调用手册

本文说明客户端程序在什么位置调用 AuthNexus SDK,以及每个阶段应该调用哪些接口。接入包下载、库文件链接和 Demo 运行步骤见 C/C++ SDK 对接

连接目标

SDK 连接的是节点服务地址,不是控制面板地址、管理后台页面地址,也不是 /admin/v1/ 这类 Admin API 地址。

典型调用顺序

客户端程序通常按下面顺序接入:

  1. 程序启动时准备 SDKConfig
  2. 注册推送和会话终止回调。
  3. 调用 connect() 连接节点服务。
  4. 在登录窗口或自动登录流程里调用 login()card_key_login()
  5. 登录成功后开启 enable_heartbeat(),并按需开启 enable_context_sync()
  6. 在业务页面中调用云变量、云函数、用户信息、公告、云文件等接口。
  7. 程序退出、用户登出或切换账号时调用 disable_heartbeat()disable_context_sync()logout()disconnect()

准备配置

如果你使用管理后台下载的接入包,优先使用接入包提供的配置入口。示例中的 license_secrets.h 会生成 make_app_config(),只需要传入节点服务地址和端口:

cpp
#include "authnexus_sdk.h"
#include "license_secrets.h"

namespace sdk = auth_nexus::sdk;

sdk::Client make_client()
{
    auto cfg = authnexus::license::make_app_config("node.example.com", 8443);

    cfg.device_id = sdk::compute_device_id("your-app-or-product-name");
    cfg.build_id = "your-build-id"; // 使用 QVM/CloudGate 时填写实际 Build ID

    return sdk::Client(cfg);
}

device_id 用于设备绑定和换绑判断。建议使用稳定的产品标识作为 compute_device_id() 的 salt,并在同一产品线内保持不变。不要每次启动随机生成设备 ID,否则会导致同一设备被识别为不同设备。

注册回调

回调建议在 connect() 之前注册。它们通常放在客户端对象初始化之后、真正连接节点之前。

cpp
void register_callbacks(sdk::Client& client)
{
    client.on_announcement([](const sdk::PushNotification& n) {
        // 管理后台发布新公告时触发
        show_announcement(n.title, n.body);
    });

    client.on_message([](const sdk::PushNotification& n) {
        // 管理后台发送给当前用户的短消息
        show_message(n.title, n.body);
    });

    client.on_config_updated([](const sdk::PushNotification&) {
        // 云变量、云函数等配置变更后,可在这里提示业务重新拉取
        mark_config_dirty();
    });

    client.on_force_logout([](const sdk::PushNotification& n) {
        // 服务端要求当前账号下线。这里建议切到主线程处理 UI 和退出登录。
        post_to_main_thread([body = n.body] {
            show_force_logout(body);
        });
    });

    client.on_session_terminated([](sdk::TerminationReason reason) {
        // 服务到期、账号禁用、Token 失效或连接被服务端结束时触发。
        post_to_main_thread([reason] {
            show_session_ended(sdk::termination_reason_name(reason));
        });
    });
}

回调线程

推送回调在 SDK 内部线程执行。不要在回调里直接做长时间阻塞操作,也不要直接调用 disconnect();需要更新 UI、退出登录或结束进程时,请投递到你的主线程或业务线程处理。

连接节点

connect() 通常在程序启动后的授权初始化阶段调用。连接失败时,用 last_error_code()last_error() 显示或记录原因。

cpp
bool connect_node(sdk::Client& client)
{
    if (client.connect()) {
        return true;
    }

    const auto code = client.last_error_code();
    const auto msg = client.last_error();
    log_error("connect failed", static_cast<int>(code), msg);
    return false;
}

如果 connect() 失败,不要继续调用登录、云变量、云函数等业务接口。应提示用户检查网络、节点地址、端口放行或接入包是否匹配当前应用。

登录和注册

账号密码登录放在用户点击“登录”按钮之后:

cpp
sdk::AuthResult auth = client.login(username, password);
if (!auth.success) {
    if (auth.rebind_required) {
        // 设备不匹配,进入换绑确认流程。
        show_rebind_confirm(auth.rebind_quote, auth.rebind_ticket);
    } else {
        show_login_error(auth.code, auth.message);
    }
    return;
}

show_login_success(auth.username, auth.expire_time);

卡密登录适合只有卡密输入框的产品:

cpp
sdk::AuthResult auth = client.card_key_login(card_key);
if (!auth.success) {
    if (auth.rebind_required) {
        show_rebind_confirm(auth.rebind_quote, auth.rebind_ticket);
    } else {
        show_login_error(auth.code, auth.message);
    }
    return;
}

注册接口只负责创建账号。注册成功后,仍建议再调用一次 login() 建立登录状态:

cpp
auto reg = client.register_user(username, password, email, invite_code, card_key);
if (!reg.success) {
    show_register_error(reg.code, reg.message);
    return;
}

auto auth = client.login(username, password);

设备换绑

当登录返回 rebind_required == true 时,表示当前设备与账号或卡密绑定设备不一致。此时可以把 rebind_quote 展示给用户确认,再提交换绑。

账号密码登录遇到设备不匹配时,使用 rebind_device()

cpp
auto rb = client.rebind_device(
    auth.rebind_ticket,
    sdk::compute_device_id("your-app-or-product-name"),
    "user confirmed rebind");

if (!rb.success) {
    show_rebind_error(rb.code, rb.message);
    return;
}

// 换绑成功后重新登录。
auth = client.login(username, password);

卡密登录遇到设备不匹配时,使用 card_key_rebind()

cpp
auto rb = client.card_key_rebind(
    auth.rebind_ticket,
    sdk::compute_device_id("your-app-or-product-name"),
    "user confirmed card rebind");

if (!rb.success) {
    show_rebind_error(rb.code, rb.message);
    return;
}

auth = client.card_key_login(card_key);

rebind_quote 中包含本次是否可换绑、是否扣除时长、换绑后到期时间、窗口内剩余次数等信息。正式产品中建议先展示确认页,不要静默自动换绑。

登录后保活

登录成功后建议立即启动周期心跳:

cpp
client.enable_heartbeat(std::chrono::seconds(30));

心跳用于维持在线状态,并让客户端及时感知服务到期、账号禁用、拉黑、Token 失效等状态。后台心跳成功后会刷新最近一次服务到期时间:

cpp
uint64_t expire_ms = client.last_service_expire_ms();
if (expire_ms != 0) {
    update_expire_label(sdk::format_unix_ms(expire_ms));
}

如果你想在某个页面打开时立即刷新到期时间,也可以手动发送一次心跳:

cpp
auto hb = client.send_heartbeat();
if (hb.success) {
    update_expire_label(sdk::format_unix_ms(hb.service_expire_time));
}

在线状态检测

如果你的产品需要在关键操作前确认当前仍在线,可以使用 Context Sync:

cpp
// 登录成功后开启后台在线新鲜度同步。
client.enable_context_sync();

// 在普通 UI 刷新中非阻塞读取。
if (!client.context_is_current(sdk::Client::kRecommendedFreshnessSec)) {
    show_offline_hint();
}

// 在进入关键功能前做一次同步探测,最多阻塞 2 秒。
if (!client.context_probe(2000)) {
    show_offline_hint();
    return;
}

普通授权产品可以只启用心跳。使用 QVM/CloudGate 或需要强在线门槛的功能时,建议同时启用 Context Sync,并在敏感操作前调用 context_probe()

查询用户和业务配置

这些接口通常在登录成功后调用,用于刷新用户中心、套餐权益、公告、云变量和版本信息。

cpp
auto info = client.get_user_info();
if (info.success) {
    show_user(info.user_info.username, info.user_info.expire_at);
}

auto status = client.get_account_status();
if (status.success) {
    update_features(status.account_status.features);
}

auto vars = client.get_variables({"feature_x", "api_endpoint"});
if (vars.success) {
    apply_variables(vars.variables);
}

auto announcements = client.get_announcements(0, 1);
if (announcements.success && !announcements.announcements.empty()) {
    show_announcement(
        announcements.announcements.front().title,
        announcements.announcements.front().content);
}

auto version = client.get_version(sdk::VersionChannel::Stable);
if (version.success && version.found && version.version.force_update) {
    show_force_update(version.version.version, version.version.update_url);
}

如果只查询某张卡密的属性,而不是登录当前账号,可以调用:

cpp
auto attr = client.query_card_key_attribute(card_key);
if (attr.success) {
    show_card_key_attribute(attr.attribute, attr.duration_minutes);
}

云函数

云函数适合把需要服务端裁决的逻辑放到后台,例如发放动态配置、计算权益、返回一次性业务参数。

cpp
auto fn = client.call_cloud_function(
    "premium_gate",
    {
        {"scene", "startup"},
        {"user_action", "open_feature"}
    });

if (!fn.success) {
    show_cloud_function_error(fn.code, fn.message);
    return;
}

handle_cloud_result(fn.result);

云函数返回值是字符串。如果你需要结构化数据,可以让云函数返回 JSON 字符串,再由客户端自行解析。

云文件

小文件可以直接整文件下载到内存:

cpp
auto file = client.download_cloud_file("config.json");
if (file.success) {
    parse_config(file.data);
}

大文件建议下载到本地路径,避免一次性占用大量内存:

cpp
auto r = client.download_cloud_file_to_path(
    "payload.bin",
    "payload.bin",
    [](uint64_t done, uint64_t total) {
        update_download_progress(done, total);
    });

if (!r.success) {
    show_download_error(r.code, r.message);
}

如果你需要自己接收每个数据块,可以使用流式回调:

cpp
auto r = client.download_cloud_file_stream(
    "payload.bin",
    [](uint64_t offset, const uint8_t* data, std::size_t len) {
        write_chunk(offset, data, len);
    },
    [](uint64_t done, uint64_t total) {
        update_download_progress(done, total);
    });

充值和登出

已登录用户使用卡密续时长:

cpp
auto recharge = client.card_key_recharge(card_key);
if (recharge.success) {
    update_expire_label(sdk::format_unix_ms(recharge.new_expire_time));
}

未登录状态下,也可以用账号密码和卡密直接充值:

cpp
auto recharge = client.card_key_recharge(card_key, username, password);

用户主动退出登录时:

cpp
client.disable_heartbeat();
client.disable_context_sync();

auto out = client.logout();
client.disconnect();

程序退出时,即使没有显式 logout(),也应该调用 disconnect() 释放连接和后台线程。

错误处理建议

所有业务结果都遵循同一模式:

cpp
auto r = client.get_user_info();
if (!r.success) {
    if (r.code < 0) {
        // SDK 本地错误,例如未连接、网络中断、超时或响应解析失败。
        show_network_error(r.message);
    } else {
        // 服务端业务状态码,可按 StatusCode 做用户提示。
        show_business_error(r.code, r.message);
    }
    return;
}

常见状态码:

状态码含义建议处理
InvalidCredentials用户名或密码错误提示用户重新输入
DeviceMismatch当前设备与绑定设备不一致展示换绑报价并让用户确认
ServiceExpired服务到期引导充值
CardKeyDisabled (0x27)卡密已被禁用(未使用或已使用的卡被管理员禁用/作废)提示联系发卡方或客服
AccountDisabled账号被禁用提示联系客服
AccountBlacklisted账号被限制提示联系客服
InvalidToken / TokenExpired / NotAuthenticated登录状态失效回到登录页重新登录
RateLimited / TooManyAttempts请求过于频繁稍后重试

连接阶段失败使用 last_error_code()last_error();业务接口失败使用返回结果里的 codemessage

最小完整示例

cpp
#include "authnexus_sdk.h"
#include "license_secrets.h"

#include <chrono>
#include <iostream>

namespace sdk = auth_nexus::sdk;

int main()
{
    auto cfg = authnexus::license::make_app_config("node.example.com", 8443);
    cfg.device_id = sdk::compute_device_id("your-app-or-product-name");

    sdk::Client client(cfg);

    client.on_session_terminated([](sdk::TerminationReason reason) {
        std::cout << "session ended: "
                  << sdk::termination_reason_name(reason) << "\n";
    });

    if (!client.connect()) {
        std::cerr << "connect failed: " << client.last_error() << "\n";
        return 1;
    }

    auto auth = client.login("username", "password");
    if (!auth.success) {
        std::cerr << "login failed: " << auth.message << "\n";
        client.disconnect();
        return 1;
    }

    client.enable_heartbeat(std::chrono::seconds(30));

    auto vars = client.get_variables({"feature_x"});
    if (vars.success) {
        std::cout << "variables loaded: " << vars.variables.size() << "\n";
    }

    auto fn = client.call_cloud_function("echo", {{"k", "v"}});
    if (fn.success) {
        std::cout << "cloud function result: " << fn.result << "\n";
    }

    client.disable_heartbeat();
    client.disable_context_sync();
    client.disconnect();
    return 0;
}

对接位置检查清单

  • 程序初始化:准备 SDKConfig,设置 device_id,注册回调。
  • 连接节点:调用 connect();失败则停止后续授权流程。
  • 用户登录:调用 login()card_key_login()
  • 登录成功:启动 enable_heartbeat();需要强在线检测时再启动 enable_context_sync()
  • 业务页面:调用用户信息、云变量、公告、版本、云函数、云文件接口。
  • 服务到期展示:优先读 last_service_expire_ms(),需要立即刷新时再调用 send_heartbeat()
  • 设备不匹配:展示 rebind_quote,用户确认后调用对应换绑接口,再重新登录。
  • 退出账号或程序关闭:停止心跳和 Context Sync,调用 logout()disconnect()