> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velafi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 生成令牌

> 使用 API key 和 secret 对请求签名，获取业务令牌，用于在 X-BH-TOKEN 请求头中认证其他 VelaFi API 调用。

使用此接口获取业务令牌。你的服务器使用 API secret 对请求签名，并在 `X-BH-APIKEY` 请求头中发送 API key。响应包含一个 `token`（在需要认证的请求中通过 `X-BH-TOKEN` 请求头发送）和一个 `expireTime`。

> [!NOTE]
>
> 在此调用成功之前，VelaFi 必须为你的账户开通 API 访问权限，且 API key 必须配置 IP 白名单。请求必须来自该白名单中的 IP 地址。参见[认证](https://docs.velafi.com/cn/api-reference/authentication.md)。

## 接口地址

```text theme={null}
GET https://api.velafi.com/v2/token/generate
```

> [!NOTE]
>
> 在沙盒环境中测试时，请使用 `https://api-test.velafi.com/v2/token/generate` 及你的沙盒环境凭证。

## 请求头

**X-BH-APIKEY** `string` *required*

你的 API key。

## 查询参数

**timestamp** `integer` *required*

当前 Unix 时间（毫秒），例如 `1740568725231`。

**signature** `string` *required*

以 API secret 为密钥，对字符串 `timestamp=<timestamp>` 计算 HMAC-SHA256，并编码为小写十六进制。

> [!WARNING]
>
> 查询参数中只能发送 `timestamp` 和 `signature`。服务器会使用去掉 `signature` 参数后的查询字符串来验证签名。只有这两个参数时，被签名的字符串恰好为 `timestamp=<timestamp>`。

## 如何签名

**1. 构造消息**

获取当前 Unix 时间（毫秒），并构造字符串 `timestamp=<ms>`，例如 `timestamp=1740568725231`。

**2. 计算 HMAC**

以 API secret 为密钥，计算该消息的 HMAC-SHA256。

**3. 对结果进行十六进制编码**

将 32 字节的结果编码为 64 个字符的小写十六进制字符串。这就是 `signature`。

## 响应字段

**code** `integer`

成功时为 `200`。出错时返回负数代码；参见[错误](#errors)。

**msg** `string`

成功时为 `SUCCESS`，否则为错误信息。

**data** `object`

**data 字段**

**token** `string`

业务令牌。在需要认证的请求中通过 `X-BH-TOKEN` 请求头发送。

**expireTime** `string`

令牌的过期时间，为毫秒级 Unix 时间戳。请在此时间之前申请新令牌。

## 示例

```bash cURL theme={null}
API_KEY="your_api_key"
API_SECRET="your_api_secret"
TIMESTAMP=$(( $(date +%s) * 1000 ))
SIGNATURE=$(printf 'timestamp=%s' "$TIMESTAMP" | openssl dgst -sha256 -hmac "$API_SECRET" | sed 's/^.*= //')

curl --request GET \
  --url "https://api-test.velafi.com/v2/token/generate?timestamp=${TIMESTAMP}&signature=${SIGNATURE}" \
  --header "X-BH-APIKEY: ${API_KEY}"
```

```python Python theme={null}
import hashlib
import hmac
import time

import requests

BASE_URL = "https://api-test.velafi.com"
API_KEY = "your_api_key"
API_SECRET = "your_api_secret"


def generate_token():
    timestamp = str(int(time.time() * 1000))
    signature = hmac.new(
        API_SECRET.encode("utf-8"),
        f"timestamp={timestamp}".encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

    response = requests.get(
        f"{BASE_URL}/v2/token/generate",
        params={"timestamp": timestamp, "signature": signature},
        headers={"X-BH-APIKEY": API_KEY},
        timeout=10,
    )
    body = response.json()
    if body.get("code") != 200:
        raise RuntimeError(f"Token request failed: {body}")
    return body["data"]["token"], int(body["data"]["expireTime"])


token, expire_time_ms = generate_token()
print(token, expire_time_ms)
```

```javascript Node.js theme={null}
const crypto = require("crypto");

const BASE_URL = "https://api-test.velafi.com";
const API_KEY = "your_api_key";
const API_SECRET = "your_api_secret";

async function generateToken() {
  const timestamp = Date.now().toString();
  const signature = crypto
    .createHmac("sha256", API_SECRET)
    .update(`timestamp=${timestamp}`)
    .digest("hex");

  const url = `${BASE_URL}/v2/token/generate?timestamp=${timestamp}&signature=${signature}`;
  const response = await fetch(url, { headers: { "X-BH-APIKEY": API_KEY } });
  const body = await response.json();
  if (body.code !== 200) {
    throw new Error(`Token request failed: ${JSON.stringify(body)}`);
  }
  return { token: body.data.token, expireTime: Number(body.data.expireTime) };
}

generateToken().then(({ token, expireTime }) => console.log(token, expireTime));
```

```java Java theme={null}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class GenerateToken {
    private static final String BASE_URL = "https://api-test.velafi.com";
    private static final String API_KEY = "your_api_key";
    private static final String API_SECRET = "your_api_secret";

    public static void main(String[] args) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis());
        String signature = hmacSha256Hex(API_SECRET, "timestamp=" + timestamp);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(BASE_URL + "/v2/token/generate?timestamp=" + timestamp + "&signature=" + signature))
                .header("X-BH-APIKEY", API_KEY)
                .GET()
                .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
                .send(request, HttpResponse.BodyHandlers.ofString());
        // On success: {"code":200,"msg":"SUCCESS","data":{"token":"...","expireTime":"..."}}
        System.out.println(response.body());
    }

    static String hmacSha256Hex(String secret, String message) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] raw = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
        StringBuilder hex = new StringBuilder();
        for (byte b : raw) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }
}
```

```json Response theme={null}
{
  "code": 200,
  "msg": "SUCCESS",
  "data": {
    "token": "00000000-0000-0000-0000-000000000006",
    "expireTime": "1740568725231"
  }
}
```

## 错误

出错时返回 HTTP 状态码 `400` 以及负数 `code`：

| 代码 | 消息 | 原因 |
| - | - | - |
| `-1002` | You are not authorized to execute this request | 缺少 `X-BH-APIKEY` 请求头或格式错误。 |
| `-1004` | `Missing required parameter '<name>'` | 缺少 `signature` 查询参数，或在发送了其他查询参数时缺少 `timestamp`。消息中会指明缺少的参数。 |
| `-1103` | An unknown parameter was sent. | 请求没有任何查询参数，或只有 `signature` 一个参数。 |
| `-1022` | Signature for this request is not valid. | 签名不匹配，或 API key 不存在。 |
| `-1023` | Please set IP whitelist before using API | 该 API key 未配置 IP 白名单。 |
| `-2015` | Invalid API-key, IP, or permissions for action. | API key 或你的账户已被禁用，或请求 IP 地址不在该 key 的白名单中。 |
| `-5033` | You are not yet an API user. Please contact the OTC customer service manager if you have any needs. | 你的账户未开通 API 访问权限。 |

## 使用令牌

在 `X-BH-TOKEN` 请求头中发送令牌：

```bash theme={null}
curl --request GET \
  --url https://api-test.velafi.com/v2/user/account \
  --header "X-BH-TOKEN: your_token"
```

请在 `expireTime` 之前申请新令牌。使用已过期或未知的令牌进行调用将失败，并返回代码 `-1022`。


## Related topics

- [错误码](https://docs.velafi.com/cn/docs/error-codes.md)
- [沙盒环境](https://docs.velafi.com/cn/docs/sandbox.md)
- [获取资金记录](https://docs.velafi.com/cn/api-reference/merchant/retrieve-funding-records.md)
- [获取加密货币划转记录](https://docs.velafi.com/cn/api-reference/merchant/query-crypto-transfers.md)
- [创建商户](https://docs.velafi.com/cn/api-reference/merchant/create-merchant.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.