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一致)