JiYan_SDK初始化(必须) —— 三步接入

====================================================================
  一、把以下文件加进你的 C++ 工程
====================================================================

必需（一个不能少）：
  JiYan_Cloud/JiYanSdk.h
  JiYan_Cloud/JiYanSdkInternal.h
  JiYan_Cloud/json.hpp
  JiYan_Cloud/SDK_Main.cpp
  JiYan_Cloud/SDK_Init.cpp
  JiYan_Cloud/SDK_Api.cpp
  JiYan_Cloud/SDK_Function.cpp
  JiYan_Cloud/SDK_Result.cpp
  JiYan_Cloud/SDK_Heartbeat.cpp
  JiYan_Cloud/SDK_TLS/JiYanTls.h
  JiYan_Cloud/SDK_TLS/Crypto.cpp
  JiYan_Cloud/SDK_TLS/Signature.cpp
  JiYan_Cloud/SDK_TLS/Crypto/*.cpp          （5 个）
  JiYan_Cloud/SDK_TLS/Signature/*.cpp       （11 个）
  JiYan_Cloud/SDK_TLS/Net/Env_Http_TLS.cpp

不需要：OpenSSL 头文件、第三方库、C++ 标准库之外的任何依赖。

C++ 标准：C++17 起（用到了 std::thread / std::atomic）。


====================================================================
  二、选传输方式（二选一，不能同时定义）
====================================================================

【推荐】有 Activity 环境（大多数 AndLua / 正常 APK 项目）：
    编译宏加  -DJIYAN_NET_JNI
    然后在 Activity 里把 JavaVM 地址交给 SDK：

        extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
            g_JavaVM = vm;                 // SDK 内部会读这个符号（extern JavaVM *g_JavaVM）
            return JNI_VERSION_1_6;
        }

    优点：零额外库、证书校验由 Android 系统负责（HttpsURLConnection 自带）、体积最小。
    必须注意：g_JavaVM 必须先于任何业务调用赋值，否则请求直接返回
             「JIYAN_NET_JNI 已启用但 g_JavaVM 为空」。

【备选】纯 NativeActivity / 不含 JVM 的环境：
    编译宏加  -DJIYAN_NET_OPENSSL
    SDK 会 dlopen("libssl.so") 自行握手，不链接任何 .so。
    缺点：Android 上 libssl 默认不加载系统 CA，所以本实现不启用 VERIFY_PEER
          （启用会全部握手失败）。生产环境如果在意中间人，请改用 JNI 方式，
          或把域名证书打进包内自行校验证书指纹。

两个宏都不定义时默认走 JIYAN_NET_OPENSSL。
两个都定义会直接 #error 编译失败（故意设计，防止配置错乱）。


====================================================================
  三、代码接入（最小可用版本）
====================================================================

    #include "JiYan_Cloud/JiYanSdk.h"

    void init_and_verify(const std::string &device_id) {

        jy::SdkConfig cfg;
        cfg.api_base = "https://jiyan.xn--0try77c6um82w.cn";  // 必须 https
        cfg.app_id   = "10000";                                // 5 位应用 ID
        cfg.username = "JiYan_7922772";                        // developer_username
        cfg.api_key  = "<应用详情里的 api_key>";
        cfg.sign_key = "<应用详情里的 sign_key>";
        cfg.encrypt  = "4";        // 0-5，必须与应用当前配置一致
        cfg.sign     = "12";       // 0-13，同上
        // cfg.sec_pass / cfg.sec_token：应用开了「强制校验」才需要填

        jy::Result init = jy::SdkInit(cfg, device_id);
        if (!init.ok) {
            // init.message 里是具体原因（编号非法 / key 缺失 / 地址非法）
            return;
        }

        jy::Result r = jy::Use_Card("你的卡密", device_id, "玩家昵称", "玩家UID");
        if (!r.ok) {
            // r.code: 2001 卡密不存在 / 2002 已到期 / 2003 绑了别的设备
            //         2004 会话过期 / 2005 已禁用 / 1003 验签失败 / 1002 解密失败
            return;
        }

        // 成功后：
        //   r.expire_text        到期时间文案（"2027-10-20 20:44:16" 或 "永久"）
        //   r.seconds_left       剩余秒（永久为 -1）
        //   r.online             在线人数
        //   r.heartbeat_interval 心跳间隔（分钟）
        //   r.rc                 服务端回显的 rc（已比对通过）
        // SDK 已自动启动心跳线程，不用管。
    }


====================================================================
  四、编译命令参考
====================================================================

桌面/Linux 调试：

g++ -std=c++17 -O2 -pthread -DJIYAN_NET_OPENSSL \
    -I "JiYan_Cloud" -I "JiYan_Cloud/SDK_TLS" \
    main.cpp \
    JiYan_Cloud/SDK_*.cpp \
    JiYan_Cloud/SDK_TLS/Crypto.cpp \
    JiYan_Cloud/SDK_TLS/Signature.cpp \
    JiYan_Cloud/SDK_TLS/Crypto/*.cpp \
    JiYan_Cloud/SDK_TLS/Signature/*.cpp \
    JiYan_Cloud/SDK_TLS/Net/Env_Http_TLS.cpp \
    -o jiyan_demo

Android NDK（Android.mk 片段）：

    LOCAL_CPPFLAGS := -std=c++17 -pthread -DJIYAN_NET_JNI
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/JiYan_Cloud $(LOCAL_PATH)/JiYan_Cloud/SDK_TLS
    LOCAL_SRC_FILES := \
        JiYan_Cloud/SDK_Main.cpp \
        JiYan_Cloud/SDK_Init.cpp \
        ...（同上面的清单）
    LOCAL_LDLIBS := -llog


====================================================================
  五、自检（改完包必做）
====================================================================

    cd 包根目录
    g++ -std=c++17 -O2 -I "JiYan_Cloud" -I "JiYan_Cloud/SDK_TLS" \
        selftest.cpp \
        JiYan_Cloud/SDK_TLS/Crypto.cpp \
        JiYan_Cloud/SDK_TLS/Signature.cpp \
        JiYan_Cloud/SDK_TLS/Crypto/*.cpp \
        JiYan_Cloud/SDK_TLS/Signature/*.cpp \
        -o selftest && ./selftest

    输出「通过 349 项，失败 0 项 / [OK] 全部通过」才算对。
    任何 FAIL 都是客户端实现错，不许改服务端迁就（见 文档/测试向量.md）。


====================================================================
  六、常见问题
====================================================================

Q: 报「响应解密失败（api_key / encrypt 配置不匹配）
A: 三种可能：
   1) 应用后台改过加密方式，api_key 也跟着换了 —— 重新复制最新 api_key
   2) cfg.encrypt 与服务端 apps.encrypt_format 不一致
   3) 服务端该应用被改了密钥但客户端还拿着旧的

Q: 报「响应签名校验失败」
A: cfg.sign 与 apps.sign_type 不一致，或者 sign_key 复制错了。
   签名 12/13 是 HMAC，sign_key 只当 key 用，不要拼进内容。

Q: 报「服务器返回 301 重定向」
A: 你的 api_base 用了 http:// 或用了 CDN 没覆盖的域名。
   本 SDK 一律走 https，并且拒绝跟随重定向（防止明文 POST 泄露到 80）。

Q: 心跳一直失败被锁
A: 检查 apps.heartbeat_interval（默认 30 分钟）与设备时间。
   服务端有 ±300 秒时间窗，设备时间偏差超过 5 分钟会一直 code=1004。
   客户端定时器可被改时间绕过，服务端 heartbeat_strict=1 时业务请求也会校验。

Q: 想用加密 2 但一直返回空密文
A: api_key 长度必须 >= 64 位。当前后台生成的字母表是 64 位，如果你手动填了
   不足 64 位的串，SDK 会返回空（这是故意的，禁止补齐、禁止回落明文）。

—— 极验网络