Quy chuẩn ký API POST nền tảng VS Open
Phiên bản tài liệu: V1.0
Ngày sửa đổi: 2026-03-16
Phạm vi áp dụng: Mọi API loại POST của nền tảng VS Open
1. Mô tả tài liệu
Tài liệu này định nghĩa quy chuẩn tạo và xác minh chữ ký cho API POST của nền tảng VS Open. Mọi yêu cầu gọi API POST của nền tảng phải tuân thủ quy chuẩn ký trong tài liệu; nếu không, yêu cầu sẽ bị máy chủ từ chối. Tài liệu gồm quy tắc ký cốt lõi, quy trình triển khai chuẩn hóa, ví dụ SDK đa ngôn ngữ và hướng dẫn xử lý ngoại lệ, phục vụ tham khảo khi tích hợp.
2. Định nghĩa thuật ngữ
| Thuật ngữ | Định nghĩa |
|---|---|
| API Key | Định danh caller do nền tảng cấp, tương ứng biến môi trường VS_OPEN_API_KEY, dùng trong header để nhận diện caller |
| Secret Key | Khóa ký do nền tảng cấp, tương ứng biến môi trường VS_OPEN_SECRET_KEY, chỉ dùng ký cục bộ, cấm truyền tải |
| Base URL | Địa chỉ cơ sở cổng API nền tảng, tương ứng biến môi trường VS_OPEN_API_BASE_URL, ví dụ https://api.valuescan.io/api/open/v1 |
| X-TIMESTAMP | Timestamp mili giây 13 chữ số, chống replay; máy chủ xác minh thời gian yêu cầu |
| X-SIGN | Kết quả chữ ký HMAC-SHA256, chuỗi hex chữ thường, dùng xác minh toàn vẹn yêu cầu |
| Raw Body | Body gốc của yêu cầu POST, chuỗi chưa qua bất kỳ định dạng, nén hay chỉnh sửa nào |
| Path | Đường dẫn tương đối của API, ví dụ /api/v1/order/create, nối sau Base URL khi sử dụng |
3. Mục đích ký
- Xác thực danh tính: Xác nhận nguồn yêu cầu là caller được nền tảng ủy quyền hợp lệ qua API Key;
- Chống sửa đổi: Xác minh nội dung body không bị thay đổi độc hại qua chữ ký;
- Chống replay: Giới hạn thời hạn yêu cầu qua timestamp (cửa sổ xác minh mặc định phía máy chủ là 5 phút);
- An toàn dữ liệu: Không cần truyền Secret Key, giảm rủi ro lộ khóa.
4. Ràng buộc cốt lõi
- Ràng buộc loại yêu cầu: Chỉ áp dụng cho API POST; body chỉ hỗ trợ định dạng Raw (JSON, FormData, v.v. phải tham gia ký dưới dạng chuỗi gốc);
- Ràng buộc body: Raw Body tham gia ký phải trùng khớp hoàn toàn với body thực tế gửi đi; cấm bỏ khoảng trắng, xuống dòng, chỉnh thụt lề, sắp xếp lại trường, v.v.;
- Ràng buộc timestamp: X-TIMESTAMP phải là timestamp mili giây 13 chữ số, chênh lệch với giờ máy chủ ≤ 5 phút;
- Ràng buộc mã hóa: Mọi chuỗi (body, khóa, chuỗi cần ký) dùng mã hóa UTF-8;
- Ràng buộc định dạng chữ ký: X-SIGN phải là chuỗi hex chữ thường 64 ký tự do HMAC-SHA256 tạo; cấm chuyển HOA hoặc Base64.
5. Quy trình ký
5.1 Quy trình tổng thể
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 Các bước chi tiết
Bước 1: Chuẩn bị cấu hình
Lấy API Key, Secret Key và Base URL từ console nền tảng VS Open, cấu hình thành biến môi trường hệ thống:
- Hệ thống 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)" - Hệ thống 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)
Bước 2: Tạo timestamp
Tạo timestamp mili giây 13 chữ số của thời điểm hiện tại, ví dụ:
- Python:
str(int(time.time() * 1000)) - Java:
String.valueOf(System.currentTimeMillis()) - JavaScript:
Date.now().toString()
Bước 3: Lấy body gốc
Đọc chuỗi Raw Body gốc của yêu cầu POST; nếu body rỗng thì truyền chuỗi rỗng "".
Bước 4: Ghép chuỗi cần ký
Ghép theo quy tắc cố định, không ký tự phân tách:
待签名字符串 = X-TIMESTAMP + Raw BodyBước 5: Thực thi chữ ký HMAC-SHA256
Dùng Secret Key làm khóa, thực thi mã hóa HMAC-SHA256 trên chuỗi đã ghép, tạo chuỗi hex chữ thường. Thuật toán xác minh cốt lõi phía máy chủ (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进制小写签名
}Bước 6: Tạo header yêu cầu
Thêm 3 trường sau vào header POST:
| Tên header | Nguồn giá trị |
|---|---|
| X-API-KEY | Biến môi trường VS_OPEN_API_KEY |
| X-TIMESTAMP | Timestamp mili giây 13 chữ số tạo ở bước 2 |
| X-SIGN | Chữ ký hex chữ thường tạo ở bước 5 |
Bước 7: Gửi yêu cầu
Ghép Base URL + API Path để có địa chỉ yêu cầu đầy đủ, gửi POST kèm header chữ ký và body gốc.
6. SDK tạo header ký và gọi POST
6.1 Python SDK
6.1.1 Mã đầy đủ (gồm BaseUrl + Path)
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 Hướng dẫn sử dụng
- Phụ thuộc môi trường: Python 3.6+, cần cài thư viện requests (
pip install requests); - Cấu hình: Theo mục 5.2.1, cấu hình biến môi trường
VS_OPEN_API_KEY,VS_OPEN_SECRET_KEY,VS_OPEN_API_BASE_URL; - Cách gọi:
- Import lớp
VSAPISign; - Gọi phương thức
send_post_request, truyền đường dẫn tương đối API và body gốc; - Phương thức tự ghép BaseUrl + Path, tạo chữ ký và gửi yêu cầu;
- Import lớp
- Điều chỉnh tương thích: Sửa
Content-Typetheo loại body thực tế (ví dụ form dùngapplication/x-www-form-urlencoded).
6.2 JavaScript/Node.js SDK
6.2.1 Mã đầy đủ (gồm BaseUrl + Path)
/**
* 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 Hướng dẫn sử dụng
- Phụ thuộc môi trường: Node.js 16+ (tích hợp sẵn
crypto/fetch/url, không cần cài thêm); - Cấu hình:
- Terminal 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" - Dòng lệnh Windows:cmd
set VS_OPEN_API_KEY=你的API Key set VS_OPEN_SECRET_KEY=你的Secret Key set VS_OPEN_API_BASE_URL=你的Base URL
- Terminal Linux/Mac:
- Cách gọi:
- Gọi lập trình: import hàm
vsPost, truyền đường dẫn tương đối API và dữ liệu yêu cầu; - Gọi dòng lệnh (có thể mở rộng): bọc tham số
--path/--datadựa trên logic hiện có;
- Gọi lập trình: import hàm
- Điều chỉnh tương thích: Sửa
Content-Typetheo loại body thực tế (ví dụ form dùngapplication/x-www-form-urlencoded).
7. Ví dụ đầy đủ
7.1 Cấu hình cơ bản
- 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 - Body gốc:json
{ "user_id": "U10001", "action": "create_order", "params": {"goods_id": "G001", "num": 2} } - Timestamp đã tạo:
1710585600000
7.2 Tính toán chữ ký
- Chuỗi cần ký:
1710585600000{ "user_id": "U10001", "action": "create_order", "params": {"goods_id": "G001", "num": 2} } - Thực thi chữ ký HMAC-SHA256 (dùng Secret Key), tạo chữ ký:
5f8d7e6c5b4a32109876543210abcdef5f8d7e6c5b4a32109876543210abcdef(giá trị ví dụ)
7.3 Thông tin yêu cầu đầy đủ
- URL đầy đủ:
https://api.valuescan.io/api/v1/order/create - Header:http
X-API-KEY: VS_API_20260316001 X-TIMESTAMP: 1710585600000 X-SIGN: 5f8d7e6c5b4a32109876543210abcdef5f8d7e6c5b4a32109876543210abcdef Content-Type: application/json; charset=utf-8 - Body: chuỗi JSON gốc (trùng với rawBody dùng để ký)