> For the complete documentation index, see [llms.txt](https://docs.catfee.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.catfee.io/getting-started/buy-energy-via-api-on-catfee/python.md).

# Python 调用示例

Python 调用 CatFee.IO Rest API 示例

### **前提条件**

1. [您需要一个有效的 API Key 和 API Secret](/getting-started/buy-energy-via-api-on-catfee/api-overview.md#apply-api-info)。
2. 确保您的环境已安装 `requests` 库，可以使用以下命令安装：

   ```bash
   pip install requests
   ```
3. 使用 Python 3.8 及以上版本。

### **示例代码**

```python
import base64
import hashlib
import hmac
import json
import uuid
from datetime import datetime, timezone
from urllib.parse import urlencode

import requests

API_KEY = "your_api_key"  # 请替换为您的 API Key
API_SECRET = "your_api_secret"  # 请替换为您的 API Secret
BASE_URL = "https://api.catfee.io"
TIMEOUT_SECONDS = 15


def generate_timestamp():
    """生成带毫秒的 ISO 8601 UTC 时间，例如 2026-09-19T08:08:08.888Z。"""
    return datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace(
        "+00:00", "Z"
    )


def build_request_path(path, query_params):
    """构建参与签名并实际发送的完整请求路径。"""
    if not query_params:
        return path
    query_string = urlencode(query_params)
    return f"{path}?{query_string}"


def generate_signature(timestamp, method, request_path):
    """使用 HMAC-SHA256 生成 Base64 编码的请求签名。"""
    sign_string = timestamp + method.upper() + request_path
    digest = hmac.new(
        API_SECRET.encode("utf-8"),
        sign_string.encode("utf-8"),
        hashlib.sha256,
    ).digest()
    return base64.b64encode(digest).decode("utf-8")


def send_request(method, path, query_params=None):
    """签名并发送 CatFee API 请求。"""
    method = method.upper()
    request_path = build_request_path(path, query_params)
    timestamp = generate_timestamp()
    signature = generate_signature(timestamp, method, request_path)

    headers = {
        "Content-Type": "application/json",
        "CF-ACCESS-KEY": API_KEY,
        "CF-ACCESS-SIGN": signature,
        "CF-ACCESS-TIMESTAMP": timestamp,
    }

    # request_path 必须同时用于签名和实际请求，避免参数编码或顺序不一致。
    return requests.request(
        method,
        BASE_URL + request_path,
        headers=headers,
        timeout=TIMEOUT_SECONDS,
    )


def main():
    # 示例：创建一笔能量订单。
    # 使用列表固定参数顺序；client_order_id 用于网络异常时安全重试。
    client_order_id = str(uuid.uuid4())
    query_params = [
        ("quantity", "65000"),
        ("receiver", "TRON_ADDRESS"),
        ("duration", "1h"),
        ("client_order_id", client_order_id),
        ("activate", "true"),
    ]

    try:
        print("Client Order ID:", client_order_id)
        response = send_request("POST", "/v1/order", query_params)
        print("HTTP Status:", response.status_code)
        print("Response Body:", response.text)

        # HTTP 层失败（例如 4xx、5xx）。
        response.raise_for_status()

        # CatFee API 通常返回 HTTP 200，仍需检查响应体中的业务 code。
        result = json.loads(response.text)
        if result.get("code") != 0:
            raise RuntimeError(
                f"API request failed: code={result.get('code')}, "
                f"message={result.get('msg') or result.get('sub_msg')}"
            )

        data = result.get("data") or {}
        print("Order ID:", data.get("id"))
    except requests.Timeout:
        print(
            "请求超时。请使用同一个 client_order_id 重试，避免重复下单：",
            client_order_id,
        )
    except json.JSONDecodeError as error:
        print("Response is not valid JSON:", error)
    except requests.RequestException as error:
        print("HTTP request failed:", error)
    except RuntimeError as error:
        print(error)


if __name__ == "__main__":
    main()
```

### **代码解析**

1. **`generate_timestamp()`**：\
   生成带毫秒的 ISO 8601 UTC 时间戳，用于 `CF-ACCESS-TIMESTAMP` 请求头。
2. **`build_request_path()`**：\
   将查询参数进行 URL 编码并拼接到请求路径。返回结果同时用于签名和实际请求，确保两者完全一致。
3. **`generate_signature()`**：\
   按照 `timestamp + method + requestPath` 拼接签名原文，使用 API Secret 计算 HMAC-SHA256，并对结果进行 Base64 编码。
4. **`send_request()`**：\
   构造三个 CatFee 鉴权请求头并发送请求。示例设置了 15 秒超时，避免请求无限等待。
5. **`main()`**：\
   调用 `POST /v1/order` 创建能量订单，并同时检查 HTTP 状态码和响应体中的业务 `code`。

### **注意事项**

* **API Key 和 Secret**：\
  请确保将 `API_KEY` 和 `API_SECRET` 替换为您从 CatFee.IO 获取的实际值。
* **签名路径必须与请求路径一致**：\
  查询参数的内容、URL 编码和顺序都必须一致。不要签名后再通过 `params=` 重新生成查询字符串。
* **订单参数**：\
  `quantity` 当前不得小于 `65000`，`duration` 当前仅支持 `1h`；`receiver` 必须是有效的 TRON 地址。
* **幂等重试**：\
  每笔新订单使用唯一且不超过 64 个字符的 `client_order_id`。发生超时或连接中断时，应保存并复用原值重试，不能重新生成。
* **地址激活**：\
  `activate=true` 表示必要时允许激活接收地址；如果设为 `false` 且地址未激活，接口将返回错误。
* **响应判断**：\
  HTTP `200` 不一定表示业务成功，还必须确认响应 JSON 中的 `code` 为 `0`。

### **总结**

此示例展示了如何使用 Python 调用 `POST /v1/order` 购买能量，并正确处理请求签名、幂等订单号和 API 响应。其他接口也可以复用 `send_request()`，只需传入对应的 HTTP 方法、路径和查询参数。
