快速上手
只需五个请求,即可完成从获取报价到链上能量委托的全流程:估算、签名、报价、下单、确认。其中第一步完全公开,无需任何密钥,在注册账户之前即可直接调用。
开始之前
| 阶段 | 价格估算 | 锁定报价 | 下单购买 |
|---|---|---|---|
| 请求接口 | GET /v1/estimate | POST /v1/quotes | POST /v1/orders |
| API 密钥 | 不需要 | 需要 | 需要 |
| 价格约束 | 无约束 —— 随日间时段浮动 | 有约束,锁定 120 秒 (expires_at) | 直接从账户余额扣款 |
| 生成对象 | 无 | 报价单 (qt_…) | 订单 (ord_…) |
各步骤所需准备工作:
| 步骤 | 您需要准备 |
|---|---|
| 1 | 终端命令行(自带 curl)、Node.js 18+ 或 Python 3。无需其他任何依赖。 |
| 2–5 | 注册账户并创建具备相应权限的 API 密钥。注册后即可立即创建密钥;只有步骤 4 下单时才需要账户余额。AI Agent 快速接入 详细介绍了两者的创建流程;密钥权限由您自主勾选 —— 系统没有任何预设权限。 |
建议先在 Nile 测试网演练:https://api-nile.tenergy.me/v1,测试网拥有独立的账户体系、密钥(ak_test_…)以及账本。测试网与主网存在细微差异 —— 详见 网络与环境。
迈出第一单的五个步骤
步骤 1 — 价格估算
GET /v1/estimate 是公开接口:实时查询当前 1 小时 65,000 能量的租用价格;如果接收地址尚未在 TRON 链上激活,将自动加上账户激活费用。
curl -sG https://api-nile.tenergy.me/v1/estimate \
-d resource=energy -d amount=65000 -d tier=1h \
-d receiver=TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE
const url = new URL("https://api-nile.tenergy.me/v1/estimate");
url.search = new URLSearchParams({
resource: "energy",
amount: "65000",
tier: "1h",
receiver: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
}).toString();
const estimate = await (await fetch(url)).json();
console.log(estimate.total_amount_sun, "SUN");
import json, urllib.parse, urllib.request
query = urllib.parse.urlencode({
"resource": "energy",
"amount": 65000,
"tier": "1h",
"receiver": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
})
with urllib.request.urlopen(f"https://api-nile.tenergy.me/v1/estimate?{query}") as res:
estimate = json.load(res)
print(estimate["total_amount_sun"], "SUN")
| 字段 | 含义说明 |
|---|---|
price_sun_per_unit | 当前时段该时长档位每单位能量的单价(单位:SUN)。 |
energy_amount_sun | 能量租用总额:amount × price_sun_per_unit。 |
activate_amount_sun | 链上激活费用(当 receiver 未激活时收取;未传 receiver 时为 0)。 |
total_amount_sun | 当前下单所需支付的总费用。1 TRX = 1,000,000 SUN。 |
receiver_activated | 目标地址是否已激活:true / false(未传地址时为 null)。 |
as_of | 估价计算时间。该报价不具备时效锁定效力。 |
背后的实时价格网格可通过公开接口 GET /v1/prices 查询 —— 建议从接口动态读取开放的档位及其上下限,而不是在代码中硬编码。
步骤 2 — 使用密钥进行请求签名
后续每个私有接口请求都需要携带三个头:X-API-KEY、X-API-TIMESTAMP 以及 X-API-SIGN = base64(HMAC-SHA256(secret, timestamp + METHOD + path + query + body))。下面的通用辅助函数实现了完整规范;身份鉴权 详细剖析了每个部分的原理。
export TENERGY_KEY=ak_test_… # 控制台中的密钥 ID
export TENERGY_SECRET=sk_test_… # 创建密钥时展示的 Secret
BASE=https://api-nile.tenergy.me/v1
# tenergy METHOD PATH [BODY] — PATH 相对于 /v1,可包含 query 参数
tenergy() {
local method=$1 path=$2 body=${3:-}
local ts sign
ts=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
sign=$(printf '%s' "$ts$method/v1$path$body" \
| openssl dgst -sha256 -hmac "$TENERGY_SECRET" -binary | base64)
curl -sS -X "$method" "$BASE$path" \
-H "X-API-KEY: $TENERGY_KEY" -H "X-API-TIMESTAMP: $ts" -H "X-API-SIGN: $sign" \
${body:+-H "Content-Type: application/json" --data-raw "$body"}
}
import { createHmac } from "node:crypto";
const BASE = "https://api-nile.tenergy.me/v1";
const KEY = process.env.TENERGY_KEY!; // Nile 测试网为 ak_test_…
const SECRET = process.env.TENERGY_SECRET!; // 创建密钥时展示的 Secret
export async function tenergy(method: string, pathAndQuery: string, body?: unknown) {
const raw = body === undefined ? "" : JSON.stringify(body); // 严格签署实际发送的字节串
const url = new URL(BASE + pathAndQuery);
const ts = new Date().toISOString();
const sign = createHmac("sha256", SECRET)
.update(ts + method + url.pathname + url.search + raw)
.digest("base64");
const res = await fetch(url, {
method,
headers: {
"X-API-KEY": KEY,
"X-API-TIMESTAMP": ts,
"X-API-SIGN": sign,
...(raw ? { "Content-Type": "application/json" } : {}),
},
body: raw || undefined,
});
return res.json();
}
import base64, hashlib, hmac, json, os, urllib.error, urllib.request
from datetime import datetime, timezone
from urllib.parse import urlsplit
BASE = "https://api-nile.tenergy.me/v1"
KEY, SECRET = os.environ["TENERGY_KEY"], os.environ["TENERGY_SECRET"]
def tenergy(method, path_and_query, body=None):
raw = "" if body is None else json.dumps(body, separators=(",", ":")) # 严格签署实际发送的字节串
url = BASE + path_and_query
parts = urlsplit(url)
ts = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
signed = ts + method + parts.path + (f"?{parts.query}" if parts.query else "") + raw
sign = base64.b64encode(hmac.new(SECRET.encode(), signed.encode(), hashlib.sha256).digest()).decode()
headers = {"X-API-KEY": KEY, "X-API-TIMESTAMP": ts, "X-API-SIGN": sign}
if raw:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=raw.encode() or None, method=method, headers=headers)
try:
with urllib.request.urlopen(req) as res:
return json.load(res)
except urllib.error.HTTPError as err:
return json.load(err) # 错误包结构:根据 error.slug 分支处理
调用 GET /v1/balance 测试签名函数。Secret 错误将返回 401 及 1002 invalid_signature;密钥不存在或已被注销将返回 1006 key_revoked。
步骤 3 — 获取并锁定报价 (Quote)
此步骤为可选操作。报价单会在 120 秒内锁定 total_amount_sun 总价;如果您只需要限制最高价格上限,也可以跳过此步直接在订单中传入 max_price_sun。
tenergy POST /quotes '{"resource":"energy","amount":65000,"tier":"1h","receiver":"TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"}'
const quote = await tenergy("POST", "/quotes", {
resource: "energy",
amount: 65000,
tier: "1h",
receiver: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
});
quote = tenergy("POST", "/quotes", {
"resource": "energy",
"amount": 65000,
"tier": "1h",
"receiver": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
})
接口返回报价单 id、total_amount_sun 以及过期时间 expires_at。已过期的报价单下单时将被拒绝并返回 3005 quote_expired —— 此时请重新获取报价。
步骤 4 — 下单购买
请务必每次传入由您生成的唯一 client_order_id。重复发送完全相同的请求将直接返回已存在的订单(HTTP 200)且绝不重复扣款,因此网络超时后重新发起请求绝不会导致二次扣费。
tenergy POST /orders '{"quote_id":"qt_01J9Z5NB2K4R","client_order_id":"payout-8821"}' # 步骤 3 获取的 ID
const order = await tenergy("POST", "/orders", {
quote_id: quote.id,
client_order_id: "payout-8821",
});
order = tenergy("POST", "/orders", {"quote_id": quote["id"], "client_order_id": "payout-8821"})
返回 HTTP 201 代表订单已创建且扣款成功,并不代表链上委托已完成。通常状态 status 会立即变为 active;如果显示为 allocating 则说明系统正在调度池子进行委托分配。
步骤 5 — 等待订单确认
您可以使用自己的业务单号轮询订单,也可以配置 Webhook 自动接收 order.confirmed 事件通知。
tenergy GET /orders/cid:payout-8821
const current = await tenergy("GET", "/orders/cid:payout-8821");
if (current.partial) console.log("已交付", current.delivered_amount, "目标总量", current.amount);
current = tenergy("GET", "/orders/cid:payout-8821")
if current.get("partial"):
print("已交付", current["delivered_amount"], "目标总量", current["amount"])
订单状态 status | 业务含义与应对措施 |
|---|---|
allocating, delegated | 正在链上委托中 —— 请隔 1 秒再次轮询。 |
confirmed, active | 能量已成功委托至接收地址。两者可等同视为交付完成。 |
failed | 委托失败;所有预扣款项已全部原路退回账户余额。 |
expired, reclaimed | 租用时长已到期,委托已结束。 |
请注意检查 partial 字段。部分交付的订单状态仍会显示为 confirmed/active,但其实际交付能量 delivered_amount 小于请求的 amount,未交付差额已自动退回 —— 这是一个布尔字段,而非独立的状态枚举。