Skip to content

VS开放平台 POST接口签名规范

文档版本:V1.0 修订日期:2026-03-16 适用范围:VS开放平台所有 POST 类型 API 接口

1. 文档说明

本文档定义了VS开放平台POST接口的签名生成与校验规范,所有调用平台POST接口的请求必须遵循本规范完成签名,否则请求将被服务端拒绝。文档包含签名核心规则、标准化实现流程、多语言SDK代码示例及异常排查方案,供开发者对接参考。

2. 术语定义

术语定义
API Key平台分配的调用方身份标识,对应环境变量 VS_OPEN_API_KEY,用于请求头标识调用方
Secret Key平台分配的签名密钥,对应环境变量 VS_OPEN_SECRET_KEY,仅用于本地签名,禁止传输
Base URL平台API网关基础地址,对应环境变量 VS_OPEN_API_BASE_URL,如https://api.valuescan.io/api/open/v1
X-TIMESTAMP13位毫秒级时间戳,用于防重放攻击,服务端校验请求时间有效性
X-SIGNHMAC-SHA256算法生成的签名结果,16进制小写字符串,用于验证请求完整性
Raw BodyPOST请求的原始请求体,未经过任何格式化、压缩、修改的字符串
PathAPI接口相对路径,如/api/v1/order/create,需拼接Base URL使用

3. 签名目的

  • 身份认证:通过API Key确认请求来源为平台授权的合法调用方;
  • 防篡改:通过签名验证请求体内容未被恶意修改;
  • 防重放:通过时间戳限制请求有效期(服务端默认校验窗口为5分钟);
  • 数据安全:无需传输Secret Key,降低密钥泄露风险。

4. 核心约束

  1. 请求类型约束:仅适用于POST接口,请求体仅支持Raw格式(如JSON、FormData等需以原始字符串参与签名);
  2. 请求体约束:参与签名的Raw Body必须与实际发送的请求体完全一致,禁止做去空格、换行、缩进调整、字段重排序等任何修改;
  3. 时间戳约束:X-TIMESTAMP必须为13位毫秒级时间戳,且与服务端时间差值≤5分钟;
  4. 编码约束:所有字符串(请求体、密钥、待签名字符串)均采用UTF-8编码;
  5. 签名格式约束:X-SIGN必须为HMAC-SHA256算法生成的64位16进制小写字符串,禁止转换为大写或Base64格式。

5. 签名流程

5.1 整体流程

mermaid
flowchart TD
    A[获取API Key/Secret Key/Base URL] --> B[生成13位毫秒时间戳]
    B --> C[获取POST原始请求体Raw Body]
    C --> D[拼接:待签名字符串=时间戳+Raw Body]
    D --> E[使用Secret Key执行HMAC-SHA256签名]
    E --> F[签名结果转16进制小写字符串]
    F --> G[构造请求头:X-API-KEY/X-TIMESTAMP/X-SIGN]
    G --> H[拼接Base URL+Path,发送POST请求]

5.2 详细步骤

步骤1:准备配置项

VS开放平台控制台获取API Key、Secret Key和Base URL,并配置为系统环境变量:

  • Linux/Mac系统:
    bash
    export VS_OPEN_API_KEY="你的API Key"
    export VS_OPEN_SECRET_KEY="你的Secret Key"
    export VS_OPEN_API_BASE_URL="你的Base URL(如https://api.valuescan.io/api/open/v1)"
  • Windows系统:
    cmd
    set VS_OPEN_API_KEY=你的API Key
    set VS_OPEN_SECRET_KEY=你的Secret Key
    set VS_OPEN_API_BASE_URL=你的Base URL(如https://api.valuescan.io/api/open/v1)

步骤2:生成时间戳

生成当前时间的13位毫秒级时间戳,示例:

  • Python:str(int(time.time() * 1000))
  • Java:String.valueOf(System.currentTimeMillis())
  • JavaScript:Date.now().toString()

步骤3:获取原始请求体

读取POST请求的Raw Body原始字符串,若请求体为空则传空字符串""

步骤4:拼接待签名字符串

按固定规则拼接,无分隔符:

待签名字符串 = X-TIMESTAMP + Raw Body

步骤5:执行HMAC-SHA256签名

使用Secret Key作为密钥,对拼接后的字符串执行HMAC-SHA256加密,生成16进制小写字符串。 服务端核心校验算法(Java):

java
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import java.nio.charset.StandardCharsets;

public static String hmacSign(String content, String hmacKey) {
    if (content == null || content.isEmpty() || hmacKey == null || hmacKey.isEmpty()) {
        throw new IllegalArgumentException("签名内容和密钥不能为空");
    }
    HMac hMac = new HMac(HmacAlgorithm.HmacSHA256, hmacKey.getBytes(StandardCharsets.UTF_8));
    return hMac.digestHex(content); // 生成16进制小写签名
}

步骤6:构造请求头

将以下3个字段加入POST请求头:

请求头名称取值来源
X-API-KEY环境变量 VS_OPEN_API_KEY
X-TIMESTAMP步骤2生成的13位毫秒时间戳
X-SIGN步骤5生成的16进制小写签名

步骤7:发送请求

拼接Base URL + API Path得到完整请求地址,携带签名请求头和原始请求体发送POST请求。

6. 签名Header生成+POST调用SDK

6.1 Python SDK

6.1.1 完整代码(含BaseUrl+Path调用)

python
import os
import hmac
import hashlib
import time
import requests
from urllib.parse import urljoin

# 全局配置 - 从环境变量读取Base URL
BASE_URL = os.getenv("VS_OPEN_API_BASE_URL")
if not BASE_URL:
    raise EnvironmentError("环境变量 VS_OPEN_API_BASE_URL 未配置,请先配置平台基础地址")


class VSAPISign:
    """VS开放平台POST接口签名+请求工具类"""

    @staticmethod
    def get_sign_headers(raw_body: str) -> dict:
        """
        生成POST接口签名请求头
        
        Args:
            raw_body: POST原始请求体字符串(保持原始格式,无任何修改)
        
        Returns:
            dict: 包含X-API-KEY、X-TIMESTAMP、X-SIGN的请求头字典
        
        Raises:
            ValueError: 环境变量未配置API Key/Secret Key时抛出
        """
        # 从环境变量读取密钥
        api_key = os.getenv("VS_OPEN_API_KEY")
        secret_key = os.getenv("VS_OPEN_SECRET_KEY")

        # 密钥校验
        if not api_key or not secret_key:
            raise ValueError(
                "环境变量配置异常:未检测到VS_OPEN_API_KEY或VS_OPEN_SECRET_KEY\n"
                "Linux/Mac配置命令:\n"
                "export VS_OPEN_API_KEY='你的API Key' && export VS_OPEN_SECRET_KEY='你的Secret Key'\n"
                "Windows配置命令:\n"
                "set VS_OPEN_API_KEY=你的API Key && set VS_OPEN_SECRET_KEY=你的Secret Key"
            )

        # 生成13位毫秒时间戳
        timestamp = str(int(time.time() * 1000))

        # 拼接待签名字符串
        sign_content = timestamp + raw_body

        # HMAC-SHA256签名(UTF-8编码),生成16进制小写字符串
        hmac_obj = hmac.new(
            secret_key.encode("utf-8"),
            sign_content.encode("utf-8"),
            digestmod=hashlib.sha256
        )
        sign = hmac_obj.hexdigest()

        # 构造签名请求头
        return {
            "X-API-KEY": api_key,
            "X-TIMESTAMP": timestamp,
            "X-SIGN": sign,
            "Content-Type": "application/json; charset=utf-8"
        }

    @staticmethod
    def send_post_request(path: str, raw_body: str, timeout: int = 10) -> requests.Response:
        """
        拼接BaseUrl+Path,发送带签名的POST请求
        
        Args:
            path: API接口相对路径(如/api/v1/order/create)
            raw_body: POST原始请求体字符串
            timeout: 请求超时时间(秒),默认10秒
        
        Returns:
            requests.Response: 接口响应对象
        
        Raises:
            requests.exceptions.RequestException: 请求失败时抛出
        """
        # 拼接完整URL(自动处理Path开头是否有/的问题)
        full_url = urljoin(BASE_URL, path)
        
        # 生成签名请求头
        headers = VSAPISign.get_sign_headers(raw_body)
        
        # 发送POST请求
        response = requests.post(
            url=full_url,
            headers=headers,
            data=raw_body.encode("utf-8"),
            timeout=timeout
        )
        return response


# 调用示例
if __name__ == "__main__":
    # 1. API接口相对路径
    api_path = "/api/v1/order/create"
    
    # 2. 原始请求体(保持与实际发送的完全一致)
    raw_body = """{
        "order_no": "ORD20260316001",
        "amount": 100.00,
        "product_id": "P10001"
    }"""

    try:
        # 3. 发送带签名的POST请求
        response = VSAPISign.send_post_request(api_path, raw_body)
        
        # 4. 处理响应
        print(f"响应状态码:{response.status_code}")
        print(f"响应内容:{response.text}")
    except Exception as e:
        print(f"请求失败:{str(e)}")

6.1.2 使用说明

  1. 环境依赖:Python 3.6+,需安装requests库(pip install requests);
  2. 配置项:按5.2.1节配置VS_OPEN_API_KEYVS_OPEN_SECRET_KEYVS_OPEN_API_BASE_URL环境变量;
  3. 调用方式
    • 导入VSAPISign类;
    • 调用send_post_request方法,传入API相对路径原始请求体
    • 方法自动拼接BaseUrl+Path,生成签名并发送请求;
  4. 适配调整:根据实际请求体类型修改Content-Type(如表单请求体改为application/x-www-form-urlencoded)。

6.2 JavaScript/Node.js SDK

6.2.1 完整代码(含BaseUrl+Path调用)

javascript
/**
 * VS开放平台POST接口签名+请求工具(Node.js)
 * 依赖:Node.js 16+(内置crypto/fetch/url,无需额外安装依赖)
 */
const crypto = require('crypto');
const process = require('process');
const { URL } = require('url');

// 全局配置 - 从环境变量读取Base URL
const BASE_URL = process.env.VS_OPEN_API_BASE_URL;
if (!BASE_URL) {
    throw new Error("环境变量 VS_OPEN_API_BASE_URL 未配置,请先配置平台基础地址");
}

/**
 * 生成符合规范的签名请求头
 * @param {string} rawBody POST原始请求体字符串(保持原始格式)
 * @returns {object} 包含X-API-KEY、X-TIMESTAMP、X-SIGN的请求头字典
 * @throws {Error} 密钥未配置时抛出异常
 */
function buildSignHeader(rawBody) {
    // 从环境变量读取密钥
    const apiKey = process.env.VS_OPEN_API_KEY;
    const secretKey = process.env.VS_OPEN_SECRET_KEY;

    // 密钥校验
    if (!apiKey) {
        throw new Error('环境变量 VS_OPEN_API_KEY 未配置,请检查配置');
    }
    if (!secretKey) {
        throw new Error('环境变量 VS_OPEN_SECRET_KEY 未配置,请检查配置');
    }

    // 生成13位毫秒时间戳
    const timestamp = Date.now().toString();

    // 拼接待签名字符串(时间戳 + 原始请求体)
    const signContent = timestamp + rawBody;

    // HMAC-SHA256签名,生成16进制小写字符串
    const hmac = crypto.createHmac('sha256', secretKey);
    hmac.update(signContent, 'utf8');
    const sign = hmac.digest('hex');

    // 构造请求头
    return {
        'X-API-KEY': apiKey,
        'X-TIMESTAMP': timestamp,
        'X-SIGN': sign,
        'Content-Type': 'application/json; charset=utf-8',
        'Accept': '*/*'
    };
}

/**
 * 拼接BaseUrl+Path,发送带签名的POST请求
 * @param {string} path API接口相对路径(如/api/v1/order/create)
 * @param {object|string} data 请求数据(JSON对象/原始字符串)
 * @param {number} timeout 超时时间(毫秒),默认10000毫秒
 * @returns {Promise<object>} API响应结果(JSON对象)
 * @throws {Error} 请求失败/超时/响应异常时抛出
 */
async function vsPost(path, data, timeout = 10000) {
    // 统一处理请求体为原始字符串
    const rawBody = typeof data === 'object' ? JSON.stringify(data) : data;

    // 拼接完整URL(自动处理Path开头是否有/的问题)
    const fullUrl = new URL(path, BASE_URL).href;

    // 生成签名请求头
    const headers = buildSignHeader(rawBody);

    // 配置超时控制器
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

    try {
        // 发送POST请求
        const response = await fetch(fullUrl, {
            method: 'POST',
            headers: headers,
            body: rawBody,
            signal: controller.signal
        });

        clearTimeout(timeoutId);

        // 校验HTTP状态码
        if (!response.ok) {
            throw new Error(`HTTP请求失败,状态码:${response.status},状态信息:${response.statusText}`);
        }

        // 解析响应结果
        return await response.json();
    } catch (error) {
        clearTimeout(timeoutId);
        if (error.name === 'AbortError') {
            throw new Error(`请求超时(${timeout}ms):${fullUrl}`);
        }
        throw new Error(`请求失败:${error.message}`);
    }
}

// 模块导出(支持其他文件引用)
module.exports = { buildSignHeader, vsPost };

// 调用示例
if (require.main === module) {
    // 1. API接口相对路径
    const apiPath = '/api/v1/order/create';
    
    // 2. 请求数据(JSON对象,内部自动转为原始字符串)
    const requestData = {
        order_no: "ORD20260316001",
        amount: 100.00,
        product_id: "P10001"
    };

    // 3. 发送请求
    vsPost(apiPath, requestData)
        .then(result => {
            console.log('请求成功,响应结果:');
            console.log(JSON.stringify(result, null, 2));
        })
        .catch(error => {
            console.error('请求失败:', error.message);
        });
}

6.2.2 使用说明

  1. 环境依赖:Node.js 16+(内置crypto/fetch/url,无需额外安装依赖);
  2. 配置项
    • Linux/Mac终端:
      bash
      export VS_OPEN_API_KEY="你的API Key"
      export VS_OPEN_SECRET_KEY="你的Secret Key"
      export VS_OPEN_API_BASE_URL="你的Base URL"
    • Windows命令行:
      cmd
      set VS_OPEN_API_KEY=你的API Key
      set VS_OPEN_SECRET_KEY=你的Secret Key
      set VS_OPEN_API_BASE_URL=你的Base URL
  3. 调用方式
    • 编程式调用:导入vsPost函数,传入API相对路径请求数据
    • 命令行调用(可扩展):基于现有逻辑封装命令行参数(--path/--data);
  4. 适配调整:根据实际请求体类型修改Content-Type(如表单请求体改为application/x-www-form-urlencoded)。

7. 完整示例

7.1 基础配置

  • Base URL:https://api.valuescan.io/api/open/v1
  • API Key:VS_API_20260316001
  • Secret Key:VS_SECRET_8e9f7d6c5b4a3210
  • API Path:/api/v1/order/create
  • 原始请求体:
    json
    {
        "user_id": "U10001",
        "action": "create_order",
        "params": {"goods_id": "G001", "num": 2}
    }
  • 生成的时间戳:1710585600000

7.2 签名计算

  1. 待签名字符串:
    1710585600000{
        "user_id": "U10001",
        "action": "create_order",
        "params": {"goods_id": "G001", "num": 2}
    }
  2. 执行HMAC-SHA256签名(使用Secret Key),生成签名: 5f8d7e6c5b4a32109876543210abcdef5f8d7e6c5b4a32109876543210abcdef(示例值)

7.3 完整请求信息

  • 完整URL:https://api.valuescan.io/api/v1/order/create
  • 请求头:
    http
    X-API-KEY: VS_API_20260316001
    X-TIMESTAMP: 1710585600000
    X-SIGN: 5f8d7e6c5b4a32109876543210abcdef5f8d7e6c5b4a32109876543210abcdef
    Content-Type: application/json; charset=utf-8
  • 请求体:原始JSON字符串(与待签名的rawBody一致)