host

正式环境:https://isv.lingxiaodata.com


响应格式

所有接口返回 JSON 结构如下:

{
  "code": 0,  // `0` 表示成功;非 `0` 表示失败  
  "message": "请求成功",
  "data": {
               // 返回业务数据结构;失败时为null
           },
  "traceId": "abc123..." //链路 ID,反馈问题时请一并提供
}

请求头签名认证

每个接口请求都必须携带以下 4 个 HTTP Header:

Header 说明
X-App-Key BFS系统下发的 appKey
X-Timestamp 当前毫秒时间戳(与服务器差值需在 ±5 分钟内)
X-Nonce 随机字符串(如 UUID),确保系统内唯一即可
X-Sign 请求签名,小写十六进制(签名需要用到 appSecret)

X-Sign签名计算步骤描述

1. 准备 method(大写,如 POST、GET)
2. 准备 path(完整路径,不含域名与 query,如 /api/isv/v1/org/trial)
3. 准备 sortedQuery(query 参数按 key 字典序拼接 k1=v1&k2=v2,无 query 则为空串;
   **value 使用解码后的原始字符串,不要 percent-encode**;中文等字符按 UTF-8 原文参与签名)
4. 计算 bodySha256 = SHA256(原始请求体字符串) 的小写 hex;无 body 时对 "" 做 SHA256
5. 拼接 canonicalString(6 行,用换行符 \n 连接):

   method + "\n" + path + "\n" + sortedQuery + "\n" + bodySha256 + "\n" + timestamp + "\n" + nonce

6. X-Sign = HMAC-SHA256(appSecret, canonicalString) 的小写 hex
  • GET、DELETE 等无body内容时,请使用空字符串 "",其SHA256(小写 hex)值为:
    e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

X-Sign签名示例数据和代码

以 注册账号 接口为例,有如下参数:

项 值
appKey xaq7xbwswzcap1ka2tylajeje2jl0czg
appSecret q4zs3qlzs7wig6wqt9eewbv7y10kbwrs
method POST
path /api/isv/v1/org/trial
sortedQuery (空串,本接口无 query)
body {"thirdOrgName":"示例公司","thirdUserName":"张三","thirdRootUserId":"your-user-001"}
bodySha256 9da29681b7c7c7b35c3f86c6723bb8f62b0298f55de42d67b9f514c8d8ab10e5
timestamp 1788588527954
nonce nvbrt0mj16vjljzbgmlyp8t6yepun4q6
最终X-Sign 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712

curl请求代码

curl -X POST 'https://isv.lingxiaodata.com/api/isv/v1/org/trial' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Key: xaq7xbwswzcap1ka2tylajeje2jl0czg' \
  -H 'X-Timestamp: 1788588527954' \
  -H 'X-Nonce: nvbrt0mj16vjljzbgmlyp8t6yepun4q6' \
  -H 'X-Sign: 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712' \
  -d '{"thirdOrgName":"示例公司","thirdUserName":"张三","thirdRootUserId":"your-user-001"}'
  • 发起请求时注意: 每次请求使用新的 X-Nonce 和当前 X-Timestamp(上文示例值为固定演示,实际调用请替换)

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class IsvSignDemo {

  public static String sha256Hex(String body) throws Exception {
    if (body == null) body = "";
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    return toHex(md.digest(body.getBytes(StandardCharsets.UTF_8)));
  }

  public static String sign(String appSecret, String method, String path,
                            String sortedQuery, String bodySha256,
                            String timestamp, String nonce) throws Exception {
    String canonical = method + "\n" + path + "\n" + sortedQuery + "\n"
            + bodySha256 + "\n" + timestamp + "\n" + nonce;
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
    return toHex(mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
  }

  /** query 参数按 key 字典序拼接 */
  public static String sortedQuery(Map<String, String> params) {
    if (params == null || params.isEmpty()) return "";
    return new TreeMap<>(params).entrySet().stream()
            .map(e -> e.getKey() + "=" + e.getValue())
            .collect(Collectors.joining("&"));
  }

  private static String toHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder(bytes.length * 2);
    for (byte b : bytes) {
      sb.append(String.format("%02x", b));
    }
    return sb.toString();
  }

  public static void main(String[] args) throws Exception {
    String body = "{\"thirdOrgName\":\"示例公司\",\"thirdUserName\":\"张三\",\"thirdRootUserId\":\"your-user-001\"}";
    String sign = sign("q4zs3qlzs7wig6wqt9eewbv7y10kbwrs", "POST", "/api/isv/v1/org/trial",
            "", sha256Hex(body), "1788588527954", "nvbrt0mj16vjljzbgmlyp8t6yepun4q6");
    System.out.println(sign);
    // 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712
  }
}

Python

import hashlib
import hmac


def sha256_hex(body: str | None) -> str:
  data = (body or "").encode("utf-8")
  return hashlib.sha256(data).hexdigest()


def sorted_query(params: dict[str, str] | None) -> str:
  """按 key 字典序拼接;value 用原始字符串,不要 urlencode。"""
  if not params:
    return ""
  return "&".join(f"{k}={v}" for k, v in sorted(params.items()))


def sign(app_secret: str, method: str, path: str, sorted_query: str,
         body_sha256: str, timestamp: str, nonce: str) -> str:
  canonical = "\n".join([method, path, sorted_query, body_sha256, timestamp, nonce])
  return hmac.new(
    app_secret.encode("utf-8"),
    canonical.encode("utf-8"),
    hashlib.sha256,
  ).hexdigest()


if __name__ == "__main__":
  body = '{"thirdOrgName":"示例公司","thirdUserName":"张三","thirdRootUserId":"your-user-001"}'
  s = sign(
    "q4zs3qlzs7wig6wqt9eewbv7y10kbwrs", "POST", "/api/isv/v1/org/trial",
    "", sha256_hex(body), "1788588527954", "nvbrt0mj16vjljzbgmlyp8t6yepun4q6",
  )
  print(s)
  # 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712

Node.js

const crypto = require("crypto");

function sha256Hex(body) {
  return crypto.createHash("sha256").update(body || "", "utf8").digest("hex");
}

function sortedQuery(params) {
  if (!params || Object.keys(params).length === 0) return "";
  return Object.keys(params)
          .sort()
          .map((k) => `${k}=${params[k]}`)
          .join("&");
}

function sign(appSecret, method, path, sortedQueryStr, bodySha256, timestamp, nonce) {
  const canonical = [method, path, sortedQueryStr, bodySha256, timestamp, nonce].join("\n");
  return crypto.createHmac("sha256", appSecret).update(canonical, "utf8").digest("hex");
}

const body = '{"thirdOrgName":"示例公司","thirdUserName":"张三","thirdRootUserId":"your-user-001"}';
const xSign = sign(
        "q4zs3qlzs7wig6wqt9eewbv7y10kbwrs", "POST", "/api/isv/v1/org/trial",
        "", sha256Hex(body), "1788588527954", "nvbrt0mj16vjljzbgmlyp8t6yepun4q6"
);
console.log(xSign);
// 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712

PHP

<?php

function sha256_hex(?string $body): string {
    return hash('sha256', $body ?? '');
}

function sorted_query(array $params): string {
    if (empty($params)) {
        return '';
    }
    ksort($params);
    $parts = [];
    foreach ($params as $k => $v) {
        $parts[] = $k . '=' . $v;
    }
    return implode('&', $parts);
}

function sign(string $appSecret, string $method, string $path, string $sortedQuery,
              string $bodySha256, string $timestamp, string $nonce): string {
    $canonical = implode("\n", [$method, $path, $sortedQuery, $bodySha256, $timestamp, $nonce]);
    return hash_hmac('sha256', $canonical, $appSecret);
}

$body = '{"thirdOrgName":"示例公司","thirdUserName":"张三","thirdRootUserId":"your-user-001"}';
$xSign = sign('q4zs3qlzs7wig6wqt9eewbv7y10kbwrs', 'POST', '/api/isv/v1/org/trial',
    '', sha256_hex($body), '1788588527954', 'nvbrt0mj16vjljzbgmlyp8t6yepun4q6');
echo $xSign . PHP_EOL;
// 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712

Go

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "sort"
    "strings"
)

func sha256Hex(body string) string {
    h := sha256.Sum256([]byte(body))
    return hex.EncodeToString(h[:])
}

func sortedQuery(params map[string]string) string {
    if len(params) == 0 {
        return ""
    }
    keys := make([]string, 0, len(params))
    for k := range params {
        keys = append(keys, k)
    }
    sort.Strings(keys)
    parts := make([]string, 0, len(keys))
    for _, k := range keys {
        parts = append(parts, k+"="+params[k])
    }
    return strings.Join(parts, "&")
}

func sign(appSecret, method, path, sortedQuery, bodySha256, timestamp, nonce string) string {
    canonical := strings.Join([]string{method, path, sortedQuery, bodySha256, timestamp, nonce}, "\n")
    m := hmac.New(sha256.New, []byte(appSecret))
    m.Write([]byte(canonical))
    return hex.EncodeToString(m.Sum(nil))
}

func main() {
    body := `{"thirdOrgName":"示例公司","thirdUserName":"张三","thirdRootUserId":"your-user-001"}`
    xSign := sign("q4zs3qlzs7wig6wqt9eewbv7y10kbwrs", "POST", "/api/isv/v1/org/trial",
        "", sha256Hex(body), "1788588527954", "nvbrt0mj16vjljzbgmlyp8t6yepun4q6")
    fmt.Println(xSign)
    // 0c1301cb88a6eea4aa34faf5936e2955c69353872a650b1775b687901ac29712
}

常见错误码

认证类(41xxx)

code 含义 建议处理
41001 缺少 ISV 认证头 检查是否携带 X-App-Key / X-Timestamp / X-Nonce / X-Sign
41002 AppKey 不存在或代理商已禁用 核对 appKey,或联系平台确认代理商状态
41003 签名校验失败 对照本文签名说明检查 path、query 排序、body 是否与发送一致
41004 请求时间戳超出容差范围 同步服务器时间或使用 NTP
41005 Nonce 重复 每次请求使用新的随机 Nonce
41006 客户端 IP 不在白名单 联系平台添加出口 IP 白名单

业务类(42xxx)

code 含义 建议处理
42001 企业不存在或未绑定 检查 oid(登录用户在我们系统的企业账号 ID)是否正确
42002 用户不存在或未绑定 检查 uid(登录用户在我们系统的登录用户 ID)是否正确
42004 参数不合法 检查接口参数必填项与格式
42005 工商企业不存在 工商信息查询 keyword 未精准匹配到企业

系统 / 内部异常类(43xxx)

code 含义 建议处理
43001 内部系统暂时不可用 稍后重试
43002 内部业务异常 根据 message 排查(常见:工号不足、账号已存在、用户已授权等)或 联系平台(提供详尽参数 和 traceId)
文档更新时间: 2026-09-16 23:42   作者:004