VMOS Cloud API
  • 简体中文
  • English
  • 简体中文
  • English
  • 产品介绍
  • 产品类型
  • 产品计费
  • OpenAPI
    • 使用指南
    • V2 简化签名
    • 接口文档
    • LLMs.txt(AI快速参考)
    • OpenAPI 规范(AI 专用)
    • 错误码
    • 实例属性列表
    • 安卓改机属性列表
    • 回调任务业务类型码
    • 更新日志
  • Android端 SDK
    • 示例搭建
    • 接口说明
    • 回调函数
    • 错误码
    • 更新日志
  • Web H5端 SDK
    • 示例搭建
    • 接口说明
    • H5 SDK 回调函数
    • 错误码
    • 更新日志
  • Windows PC端 SDK
    • 示例搭建
    • 接口说明
    • 回调函数
    • 更新日志
  • 端侧与云机通信开发
    • AIDL接入方式
    • 系统服务API(aidl)
  • 类XP、LSP Hook框架
    • 类Xposed、LSPosed框架
    • 传感器数据动态仿真
  • 相关协议

概述

V2 是 VMOSCloud OpenAPI 的简化签名方案,与原 V4(HMAC-SHA256 派生密钥)方案并存,AK / SK 共用。

已经接入 V4 的客户无需迁移,老 SDK 长期保留。新对接客户建议直接使用 V2:3 个 Header + 1 行 SHA-256 即可完成签名,不需要派生密钥、不需要重排 JSON、不需要 UTC 时间格式化。

获取账号 (AK/SK)

前置准备

在开始使用 API 前,需要获取 Access Key ID 和 Secret Access Key,用于 API 请求鉴权。

操作步骤:

  1. 登录 VMOSCloud 平台
  2. 导航至菜单:开发者 -> API
  3. 复制对应的密钥信息:
    • AccessKeyID (即 AK)
    • SecretAccessKey (即 SK)

请求 Header

每次请求 Headers 中必须包含以下 3 个鉴权参数:

参数名类型必填参数描述示例值
X-Access-Keystring必须Access Key ID 明文ak_xxxxxxxxxxxx
X-Timestampstring必须unix 秒级时间戳(10 位整数字符串),±5 分钟窗1747555200
X-Signstring必须64 位小写 hex 签名6069dfb4cb3ac57b…
Content-TypestringPOST必须资源 MIME 类型application/json

签名算法

直接字符串拼接,无分隔符:

signString = SK + X-Timestamp + path + bodyOrQuery
X-Sign     = lowerHex( SHA-256( signString_UTF8 ) )
字段取值规则
SKSecret Access Key 明文
X-Timestamp与请求头一致的 10 位 unix 秒(毫秒级会被判过期)
path含 servlet 前缀的完整路径,例如 /vcpcloud/api/padApi/padInfo
bodyOrQuery见下表

bodyOrQuery 取值规则

请求类型bodyOrQuery
GET原始 query string,例如 startDate=2026-05-01&endDate=2026-05-31;无参则空串
POST / PUT(JSON)原始 body(发什么签什么,不要重排、不要去空格)
文件上传接口 /uploadFile / /asyncCmd / /syncCmd空串(文件 body 不参与签名)

query string 的参数顺序与 URL 编码必须与实际发送的完全一致,签名前不要重排、不要二次转码。

签名示例

输入:

SK   = 9cucpjoyn4xxmkhj3q9el3ce
ts   = 1747555200
path = /vcpcloud/api/padApi/padInfo
body = {"padCode":"AC32010601132"}

拼接后的 signString:

9cucpjoyn4xxmkhj3q9el3ce1747555200/vcpcloud/api/padApi/padInfo{"padCode":"AC32010601132"}

X-Sign = lowerHex( SHA-256( signString ) ),可以用任意语言的标准 SHA-256 库验证客户端实现是否正确。

错误码

code含义
2019验证签名失败
2031无效的密钥(AccessKey 找不到)
2032请求头缺必要参数
2033时间戳过期或格式错误

调用示例

curl

TS=$(date +%s)
AK="ak_xxxxxxxxxxxx"
SK="sk_xxxxxxxxxxxx"
URI="/vcpcloud/api/padApi/padInfo"
BODY='{"padCode":"AC32010601132"}'

SIGN=$(printf "%s" "${SK}${TS}${URI}${BODY}" | openssl dgst -sha256 -hex | awk '{print $2}')

curl -X POST "https://api.vmoscloud.com${URI}" \
  -H "X-Access-Key: ${AK}" \
  -H "X-Timestamp: ${TS}" \
  -H "X-Sign: ${SIGN}" \
  -H "Content-Type: application/json" \
  --data-binary "${BODY}"

java

import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;

public class V2ApiRequestUtils {

    private static final String BASE_URL = "https://api.vmoscloud.com";
    private static final String ACCESS_KEY = "Access Key ID";
    private static final String SECRET_ACCESS_KEY = "Secret Access Key";

    public static String sendPostRequest(String path, JSONObject params) {
        try {
            String body = params == null ? "" : params.toJSONString();
            String ts = String.valueOf(System.currentTimeMillis() / 1000);

            HttpPost req = new HttpPost(BASE_URL + path);
            req.setHeader("X-Access-Key", ACCESS_KEY);
            req.setHeader("X-Timestamp", ts);
            req.setHeader("X-Sign", sign(ts, path, body));
            req.setHeader("Content-Type", "application/json");
            req.setEntity(new StringEntity(body, StandardCharsets.UTF_8));

            try (CloseableHttpClient client = HttpClients.createDefault();
                 CloseableHttpResponse resp = client.execute(req)) {
                return EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            throw new RuntimeException("V2 POST failed: " + path, e);
        }
    }

    public static String sendGetRequest(String path, JSONObject params) {
        try {
            String query = buildQuery(params);
            String ts = String.valueOf(System.currentTimeMillis() / 1000);

            HttpGet req = new HttpGet(BASE_URL + path + (query.isEmpty() ? "" : "?" + query));
            req.setHeader("X-Access-Key", ACCESS_KEY);
            req.setHeader("X-Timestamp", ts);
            req.setHeader("X-Sign", sign(ts, path, query));

            try (CloseableHttpClient client = HttpClients.createDefault();
                 CloseableHttpResponse resp = client.execute(req)) {
                return EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            throw new RuntimeException("V2 GET failed: " + path, e);
        }
    }

    private static String sign(String ts, String path, String body) throws Exception {
        String signString = SECRET_ACCESS_KEY + ts + path + body;
        byte[] raw = MessageDigest.getInstance("SHA-256")
                .digest(signString.getBytes(StandardCharsets.UTF_8));
        return String.format("%064x", new BigInteger(1, raw));
    }

    private static String buildQuery(JSONObject params) {
        if (params == null || params.isEmpty()) return "";
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> e : params.entrySet()) {
            if (sb.length() > 0) sb.append('&');
            sb.append(e.getKey()).append('=').append(e.getValue());
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        // POST 示例
        JSONObject body = new JSONObject();
        body.put("padCode", "AC32010601132");
        System.out.println(sendPostRequest("/vcpcloud/api/padApi/padInfo", body));

        // GET 示例
        JSONObject query = new JSONObject();
        query.put("startDate", "2026-05-01");
        query.put("endDate", "2026-05-31");
        System.out.println(sendGetRequest("/vcpcloud/api/padApi/getOrderEquipmentList", query));
    }
}

python

import hashlib
import time
import json
import requests

BASE_URL = "https://api.vmoscloud.com"
ACCESS_KEY = "Access Key ID"
SECRET_ACCESS_KEY = "Secret Access Key"


def _sign(ts: str, path: str, body_or_query: str) -> str:
    return hashlib.sha256(
        (SECRET_ACCESS_KEY + ts + path + body_or_query).encode("utf-8")
    ).hexdigest()


def send_post(path: str, params: dict | None = None) -> str:
    body = json.dumps(params, separators=(",", ":"), ensure_ascii=False) if params else ""
    ts = str(int(time.time()))
    headers = {
        "X-Access-Key": ACCESS_KEY,
        "X-Timestamp": ts,
        "X-Sign": _sign(ts, path, body),
        "Content-Type": "application/json",
    }
    # 用 data=bytes 避免 requests 改写 body
    resp = requests.post(BASE_URL + path, headers=headers, data=body.encode("utf-8"))
    return resp.text


def send_get(path: str, params: dict | None = None) -> str:
    query = "&".join(f"{k}={v}" for k, v in params.items()) if params else ""
    ts = str(int(time.time()))
    url = BASE_URL + path + (f"?{query}" if query else "")
    headers = {
        "X-Access-Key": ACCESS_KEY,
        "X-Timestamp": ts,
        "X-Sign": _sign(ts, path, query),
    }
    return requests.get(url, headers=headers).text


if __name__ == "__main__":
    # POST
    print(send_post("/vcpcloud/api/padApi/padInfo", {"padCode": "AC32010601132"}))
    # GET
    print(send_get("/vcpcloud/api/padApi/getOrderEquipmentList",
                   {"startDate": "2026-05-01", "endDate": "2026-05-31"}))

node

const crypto = require("crypto");

const BASE_URL = "https://api.vmoscloud.com";
const ACCESS_KEY = "Access Key ID";
const SECRET_ACCESS_KEY = "Secret Access Key";

function sign(ts, path, bodyOrQuery) {
    return crypto
        .createHash("sha256")
        .update(SECRET_ACCESS_KEY + ts + path + bodyOrQuery, "utf8")
        .digest("hex");
}

async function sendPost(path, params) {
    const body = params ? JSON.stringify(params) : "";
    const ts = Math.floor(Date.now() / 1000).toString();
    const resp = await fetch(BASE_URL + path, {
        method: "POST",
        headers: {
            "X-Access-Key": ACCESS_KEY,
            "X-Timestamp": ts,
            "X-Sign": sign(ts, path, body),
            "Content-Type": "application/json",
        },
        body,
    });
    return resp.text();
}

async function sendGet(path, params) {
    const query = params
        ? Object.entries(params).map(([k, v]) => `${k}=${v}`).join("&")
        : "";
    const ts = Math.floor(Date.now() / 1000).toString();
    const resp = await fetch(BASE_URL + path + (query ? `?${query}` : ""), {
        headers: {
            "X-Access-Key": ACCESS_KEY,
            "X-Timestamp": ts,
            "X-Sign": sign(ts, path, query),
        },
    });
    return resp.text();
}

(async () => {
    // POST
    console.log(await sendPost("/vcpcloud/api/padApi/padInfo",
        { padCode: "AC32010601132" }));
    // GET
    console.log(await sendGet("/vcpcloud/api/padApi/getOrderEquipmentList",
        { startDate: "2026-05-01", endDate: "2026-05-31" }));
})();

go

package main

import (
    "bytes"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "strconv"
    "strings"
    "time"
)

const (
    baseURL         = "https://api.vmoscloud.com"
    accessKey       = "Access Key ID"
    secretAccessKey = "Secret Access Key"
)

func sign(ts, path, bodyOrQuery string) string {
    sum := sha256.Sum256([]byte(secretAccessKey + ts + path + bodyOrQuery))
    return hex.EncodeToString(sum[:])
}

func sendPost(path string, params map[string]interface{}) (string, error) {
    body := ""
    if params != nil {
        b, _ := json.Marshal(params)
        body = string(b)
    }
    ts := strconv.FormatInt(time.Now().Unix(), 10)

    req, _ := http.NewRequest("POST", baseURL+path, bytes.NewReader([]byte(body)))
    req.Header.Set("X-Access-Key", accessKey)
    req.Header.Set("X-Timestamp", ts)
    req.Header.Set("X-Sign", sign(ts, path, body))
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    bs, _ := io.ReadAll(resp.Body)
    return string(bs), nil
}

func sendGet(path string, params map[string]string) (string, error) {
    parts := make([]string, 0, len(params))
    for k, v := range params {
        parts = append(parts, k+"="+v)
    }
    query := strings.Join(parts, "&")
    ts := strconv.FormatInt(time.Now().Unix(), 10)

    url := baseURL + path
    if query != "" {
        url += "?" + query
    }
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("X-Access-Key", accessKey)
    req.Header.Set("X-Timestamp", ts)
    req.Header.Set("X-Sign", sign(ts, path, query))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    bs, _ := io.ReadAll(resp.Body)
    return string(bs), nil
}

func main() {
    // POST
    out, _ := sendPost("/vcpcloud/api/padApi/padInfo",
        map[string]interface{}{"padCode": "AC32010601132"})
    fmt.Println(out)

    // GET
    out, _ = sendGet("/vcpcloud/api/padApi/getOrderEquipmentList",
        map[string]string{"startDate": "2026-05-01", "endDate": "2026-05-31"})
    fmt.Println(out)
}

php

<?php

const BASE_URL = "https://api.vmoscloud.com";
const ACCESS_KEY = "Access Key ID";
const SECRET_ACCESS_KEY = "Secret Access Key";

function v2_sign(string $ts, string $path, string $bodyOrQuery): string {
    return hash("sha256", SECRET_ACCESS_KEY . $ts . $path . $bodyOrQuery);
}

function send_post(string $path, ?array $params = null): string {
    $body = $params === null ? "" : json_encode($params, JSON_UNESCAPED_UNICODE);
    $ts = (string) time();

    $ch = curl_init(BASE_URL . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $body,
        CURLOPT_HTTPHEADER     => [
            "X-Access-Key: " . ACCESS_KEY,
            "X-Timestamp: " . $ts,
            "X-Sign: " . v2_sign($ts, $path, $body),
            "Content-Type: application/json",
        ],
    ]);
    $resp = curl_exec($ch);
    curl_close($ch);
    return $resp;
}

function send_get(string $path, ?array $params = null): string {
    $query = $params ? http_build_query($params) : "";
    $ts = (string) time();
    $url = BASE_URL . $path . ($query ? "?$query" : "");

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            "X-Access-Key: " . ACCESS_KEY,
            "X-Timestamp: " . $ts,
            "X-Sign: " . v2_sign($ts, $path, $query),
        ],
    ]);
    $resp = curl_exec($ch);
    curl_close($ch);
    return $resp;
}

// POST
echo send_post("/vcpcloud/api/padApi/padInfo", ["padCode" => "AC32010601132"]) . PHP_EOL;
// GET
echo send_get("/vcpcloud/api/padApi/getOrderEquipmentList",
    ["startDate" => "2026-05-01", "endDate" => "2026-05-31"]) . PHP_EOL;

.net

using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

public static class V2ApiRequestUtils
{
    private const string BaseUrl = "https://api.vmoscloud.com";
    private const string AccessKey = "Access Key ID";
    private const string SecretAccessKey = "Secret Access Key";

    private static readonly HttpClient Http = new HttpClient();

    private static string Sign(string ts, string path, string bodyOrQuery)
    {
        using var sha = SHA256.Create();
        byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(SecretAccessKey + ts + path + bodyOrQuery));
        var sb = new StringBuilder(64);
        foreach (var b in hash) sb.Append(b.ToString("x2"));
        return sb.ToString();
    }

    public static async Task<string> SendPostAsync(string path, object? body)
    {
        string bodyJson = body == null ? "" : JsonSerializer.Serialize(body);
        string ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

        var req = new HttpRequestMessage(HttpMethod.Post, BaseUrl + path)
        {
            Content = new StringContent(bodyJson, Encoding.UTF8, "application/json")
        };
        req.Headers.TryAddWithoutValidation("X-Access-Key", AccessKey);
        req.Headers.TryAddWithoutValidation("X-Timestamp", ts);
        req.Headers.TryAddWithoutValidation("X-Sign", Sign(ts, path, bodyJson));

        var resp = await Http.SendAsync(req);
        return await resp.Content.ReadAsStringAsync();
    }

    public static async Task<string> SendGetAsync(string path, Dictionary<string, string>? query)
    {
        string queryStr = query == null ? ""
            : string.Join("&", query.Select(kv => $"{kv.Key}={kv.Value}"));
        string ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

        var url = BaseUrl + path + (queryStr.Length > 0 ? "?" + queryStr : "");
        var req = new HttpRequestMessage(HttpMethod.Get, url);
        req.Headers.TryAddWithoutValidation("X-Access-Key", AccessKey);
        req.Headers.TryAddWithoutValidation("X-Timestamp", ts);
        req.Headers.TryAddWithoutValidation("X-Sign", Sign(ts, path, queryStr));

        var resp = await Http.SendAsync(req);
        return await resp.Content.ReadAsStringAsync();
    }

    public static async Task Main()
    {
        // POST
        Console.WriteLine(await SendPostAsync("/vcpcloud/api/padApi/padInfo",
            new { padCode = "AC32010601132" }));
        // GET
        Console.WriteLine(await SendGetAsync("/vcpcloud/api/padApi/getOrderEquipmentList",
            new Dictionary<string, string> { ["startDate"] = "2026-05-01", ["endDate"] = "2026-05-31" }));
    }
}

易语言

纯 Windows API(WinINet + CryptoAPI),无需第三方模块。

.版本 2

.DLL命令 CryptAcquireContext, 逻辑型, "advapi32.dll", "CryptAcquireContextA"
    .参数 phProv, 整数型, 传址
    .参数 pszContainer, 整数型
    .参数 pszProvider, 整数型
    .参数 dwProvType, 整数型
    .参数 dwFlags, 整数型

.DLL命令 CryptCreateHash, 逻辑型, "advapi32.dll", "CryptCreateHash"
    .参数 hProv, 整数型
    .参数 Algid, 整数型
    .参数 hKey, 整数型
    .参数 dwFlags, 整数型
    .参数 phHash, 整数型, 传址

.DLL命令 CryptHashData, 逻辑型, "advapi32.dll", "CryptHashData"
    .参数 hHash, 整数型
    .参数 pbData, 字节集
    .参数 dwDataLen, 整数型
    .参数 dwFlags, 整数型

.DLL命令 CryptGetHashParam, 逻辑型, "advapi32.dll", "CryptGetHashParam"
    .参数 hHash, 整数型
    .参数 dwParam, 整数型
    .参数 pbData, 字节集
    .参数 pdwDataLen, 整数型, 传址
    .参数 dwFlags, 整数型

.DLL命令 CryptDestroyHash, 逻辑型, "advapi32.dll", "CryptDestroyHash"
    .参数 hHash, 整数型

.DLL命令 CryptReleaseContext, 逻辑型, "advapi32.dll", "CryptReleaseContext"
    .参数 hProv, 整数型
    .参数 dwFlags, 整数型

.DLL命令 InternetOpen, 整数型, "wininet.dll", "InternetOpenA"
    .参数 lpszAgent, 文本型
    .参数 dwAccessType, 整数型
    .参数 lpszProxy, 整数型
    .参数 lpszProxyBypass, 整数型
    .参数 dwFlags, 整数型

.DLL命令 InternetConnect, 整数型, "wininet.dll", "InternetConnectA"
    .参数 hInternet, 整数型
    .参数 lpszServerName, 文本型
    .参数 nServerPort, 整数型
    .参数 lpszUserName, 整数型
    .参数 lpszPassword, 整数型
    .参数 dwService, 整数型
    .参数 dwFlags, 整数型
    .参数 dwContext, 整数型

.DLL命令 HttpOpenRequest, 整数型, "wininet.dll", "HttpOpenRequestA"
    .参数 hConnect, 整数型
    .参数 lpszVerb, 文本型
    .参数 lpszObjectName, 文本型
    .参数 lpszVersion, 整数型
    .参数 lpszReferrer, 整数型
    .参数 lplpszAcceptTypes, 整数型
    .参数 dwFlags, 整数型
    .参数 dwContext, 整数型

.DLL命令 HttpAddRequestHeaders, 逻辑型, "wininet.dll", "HttpAddRequestHeadersA"
    .参数 hRequest, 整数型
    .参数 lpszHeaders, 文本型
    .参数 dwHeadersLength, 整数型
    .参数 dwModifiers, 整数型

.DLL命令 HttpSendRequest, 逻辑型, "wininet.dll", "HttpSendRequestA"
    .参数 hRequest, 整数型
    .参数 lpszHeaders, 整数型
    .参数 dwHeadersLength, 整数型
    .参数 lpOptional, 字节集
    .参数 dwOptionalLength, 整数型

.DLL命令 InternetReadFile, 逻辑型, "wininet.dll", "InternetReadFile"
    .参数 hFile, 整数型
    .参数 lpBuffer, 字节集
    .参数 dwNumberOfBytesToRead, 整数型
    .参数 lpdwNumberOfBytesRead, 整数型, 传址

.DLL命令 InternetCloseHandle, 逻辑型, "wininet.dll", "InternetCloseHandle"
    .参数 hInternet, 整数型

.DLL命令 GetSystemTimeAsFileTime, , "kernel32.dll", "GetSystemTimeAsFileTime"
    .参数 lpFileTime, 长整数型, 传址

.程序集 程序集1
.程序集变量 主机, 文本型
.程序集变量 ACCESS_KEY, 文本型
.程序集变量 SECRET_ACCESS_KEY, 文本型

.子程序 _启动子程序
    主机 = “api.vmoscloud.com”
    ACCESS_KEY = “Access Key ID”
    SECRET_ACCESS_KEY = “Secret Access Key”
    调试输出 (V2请求 (“POST”, “/vcpcloud/api/padApi/padInfo”, “{"padCode":"AC32010601132"}”))
    调试输出 (V2请求 (“GET”, “/vcpcloud/api/padApi/getOrderEquipmentList”, “startDate=2026-05-01&endDate=2026-05-31”))

.子程序 取时间戳, 文本型
    .局部变量 ft, 长整数型
    GetSystemTimeAsFileTime (ft)
    返回 (到文本 (求整 (ft ÷ 10000000 - 11644473600)))

.子程序 Sha256十六进制, 文本型
    .参数 输入, 文本型
    .局部变量 hProv, 整数型
    .局部变量 hHash, 整数型
    .局部变量 数据, 字节集
    .局部变量 摘要, 字节集
    .局部变量 长度, 整数型
    .局部变量 i, 整数型
    .局部变量 b, 整数型
    .局部变量 高, 整数型
    .局部变量 十六, 文本型
    .局部变量 结果, 文本型
    十六 = “0123456789abcdef”
    数据 = 到字节集 (输入)
    .如果真 (CryptAcquireContext (hProv, 0, 0, 24, -268435456) = 假)
        返回 (“”)
    .如果真结束
    CryptCreateHash (hProv, 32780, 0, 0, hHash)
    CryptHashData (hHash, 数据, 取字节集长度 (数据), 0)
    摘要 = 取空白字节集 (32)
    长度 = 32
    CryptGetHashParam (hHash, 2, 摘要, 长度, 0)
    CryptDestroyHash (hHash)
    CryptReleaseContext (hProv, 0)
    .计次循环首 (32, i)
        b = 摘要 [i]
        高 = 求整 (b ÷ 16)
        结果 = 结果 + 取文本中间 (十六, 高 + 1, 1) + 取文本中间 (十六, b - 高 × 16 + 1, 1)
    .计次循环尾 ()
    返回 (结果)

.子程序 V2请求, 文本型
    .参数 方法, 文本型
    .参数 路径, 文本型
    .参数 数据, 文本型
    .局部变量 时间戳, 文本型
    .局部变量 签名, 文本型
    .局部变量 对象, 文本型
    .局部变量 请求头, 文本型
    .局部变量 回车换行, 文本型
    .局部变量 hOpen, 整数型
    .局部变量 hConn, 整数型
    .局部变量 hReq, 整数型
    .局部变量 标志, 整数型
    .局部变量 包体, 字节集
    .局部变量 缓冲, 字节集
    .局部变量 已读, 整数型
    .局部变量 响应, 字节集
    时间戳 = 取时间戳 ()
    签名 = Sha256十六进制 (SECRET_ACCESS_KEY + 时间戳 + 路径 + 数据)
    回车换行 = 字符 (13) + 字符 (10)
    请求头 = “X-Access-Key: ” + ACCESS_KEY + 回车换行 + “X-Timestamp: ” + 时间戳 + 回车换行 + “X-Sign: ” + 签名
    对象 = 路径
    .如果 (方法 = “GET”)
        .如果真 (数据 ≠ “”)
            对象 = 路径 + “?” + 数据
        .如果真结束
    .否则
        请求头 = 请求头 + 回车换行 + “Content-Type: application/json”
        包体 = 到字节集 (数据)
    .如果结束
    标志 = 位或 (8388608, -2147483648)
    hOpen = InternetOpen (“VMOSCloud-EYY/1.0”, 0, 0, 0, 0)
    hConn = InternetConnect (hOpen, 主机, 443, 0, 0, 3, 0, 0)
    hReq = HttpOpenRequest (hConn, 方法, 对象, 0, 0, 0, 标志, 0)
    HttpAddRequestHeaders (hReq, 请求头, -1, 536870912)
    HttpSendRequest (hReq, 0, 0, 包体, 取字节集长度 (包体))
    缓冲 = 取空白字节集 (4096)
    .判断循环首 (InternetReadFile (hReq, 缓冲, 4096, 已读) 且 已读 > 0)
        响应 = 响应 + 取字节集左边 (缓冲, 已读)
    .判断循环尾 ()
    InternetCloseHandle (hReq)
    InternetCloseHandle (hConn)
    InternetCloseHandle (hOpen)
    返回 (到文本 (响应))

常见问题

Q1:老客户是否需要迁移到 V2? 不需要。V4 长期保留,老 SDK 继续用即可。详见 V4 签名机制。

Q2:同一对 AK/SK 是否两套方案都能用? 可以。AK/SK 共用,按请求选协议。

Q3:POST body 为空时 bodyOrQuery 填什么? 空字符串。signString = SK + ts + path,末尾不再拼东西。

Q4:multipart 文件上传接口怎么签?bodyOrQuery 留空串,文件 body 不参与签名。涉及接口:/uploadFile、/asyncCmd、/syncCmd。

Q5:时间戳能用毫秒级吗? 不行。必须 10 位 unix 秒。13 位毫秒会因偏离当前时间超 5 分钟直接返回 2033。

Q6:X-Sign 大小写敏感吗? 服务端忽略大小写比较,大小写 hex 都接受。建议客户端统一输出小写。

Q7:如何分流到 V2 / V4? 带 X-Sign 头走 V2;带 Authorization 头走 V4。两套同时启用,按 Header 自动识别。

Prev
使用指南
Next
接口文档