跳至主要内容

Integrator Quote & Create Order

本文件面向 Any2Any 跨鏈聚合的集成商(Integrator),介紹以下介面:

#介面用途
1GET /api/v2/quote/stream即時詢價(SSE 串流回傳各通道報價)
2POST /api/v1/integrator/orders集成商下單(在詢價結果上建立訂單)
3POST /api/v1/integrator/orders/:orderNo/request_refund集成商對自己參與的訂單發起退款

下單時攜帶集成商 API Key 即可識別身分並完成分佣記帳,不影響一般使用者的下單流程。


0. 通用約定

0.1 Base URL

https://<aggregator-api-host>

0.2 鑑權

介面鑑權方式
/api/v2/quote/stream無需鑑權(公開詢價)
/api/v1/integrator/ordersX-Integrator-Api-Key: <api_key>(推薦)
Authorization: ApiKey <api_key>
/api/v1/integrator/orders/:orderNo/request_refundX-Integrator-Api-Key: <api_key>

API Key 由 Any2Any 後台分配,集成商可以透過集成商入口自助管理(最多 20 個)。每個 Key 都有獨立的 fee_rate(集成商抽佣費率,0.01% ~ 10.00%),下單時實際寫入的就是該 Key 的費率快照。命中 API Key 的訂單基礎手續費使用後台配置的 B 端階梯費率,使用者實際支付手續費比例 = B 端階梯費率 + 該 Key 的 fee_rate

0.3 一般回應包絡

非 SSE 介面統一回傳:

{
"code": 0,
"msg": "ok",
"data": {}
}
  • code = 0 表示成功;HTTP 狀態碼:200 成功;400 參數錯誤;401 API Key 無效;403 集成商被停用;500/502 服務端 / 上游異常。

0.4 金額表示

  • 所有 amount_in*_estimated_out_*min_acceptable_out_amount 等數量欄位,全部是目標 token 最小單位的整數字串(例如 USDT 6 位小數,"1000000" 表示 1 USDT)。
  • slippage 為小數百分比,取值範圍 (0, 1),例如 0.01 表示 1%。
  • 費率欄位(channel_feefee_rateplatform_fee_rate 等)單位為百分比(例如 0.3 即 0.3%)。

1. 詢價:GET /api/v2/quote/stream

1.1 概述

詢價採用 SSE(Server-Sent Events) 串流回傳。服務端會邊詢問上游邊推送:每個通道(channel)的報價一回傳,就以 event: channel 形式立即下發,最後以 event: done 結束。

相較於一次性回傳,串流詢價的優勢:

  • 首筆報價回應快(不必等到最慢的通道)
  • 可中途取消(關閉連線即可)
  • 錯誤隔離:單一通道失敗只發 event: error,不影響其他通道

1.2 請求

GET /api/v2/quote/stream?src_token_id=&dst_token_id=&amount_in=&slippage=&promo_code=&channel_id= HTTP/1.1
Accept: text/event-stream
Query 參數類型必填說明
src_token_idintYes來源 token id(Any2Any 內部 ID,可透過 token 列表介面取得)
dst_token_idintYes目標 token id
amount_instringYes來源 token 數量,最小單位字串(例:"1000000" 表示 1 USDT)
slippagenumberYes滑點,(0, 1) 小數,例如 0.01 = 1%
promo_codestringNo平台促銷碼(如有),命中後所有通道按折扣展示
channel_idintNo指定單一通道詢價;不傳則詢價全部啟用通道

也支援 POST 同名路徑,body 用 JSON 傳相同欄位,便於 channel_id 等數字類型。

1.3 回應

200 OKContent-Type: text/event-stream; charset=utf-8,按 SSE 協議回傳若干事件。每個事件結構如下:

event: <event-name>
data: <json-payload>

1.3.1 事件類型

event何時下發data
channel每個通道有報價時通道報價物件(見下)
error某通道詢價失敗{ channel_id?: number, msg: string }
done全部完成 / 服務端關閉{}

channel 事件 data 欄位:

{
"channel_id": 101,
"name": "Channel A",
"symbol": "USDT",
"channel_icon": "https://.../logo.png",
"estimated_out_str": "999500000",
"estimated_out_usd": "999.500000",
"channel_fee": 0.3,
"channel_fee_original": 0.3,
"promo_applied": false,
"discount_pct": null
}
欄位說明
channel_id通道 ID,下單時必須原樣回傳
name通道名稱
symbol目標 token symbol
channel_icon通道圖示 URL
estimated_out_str扣除手續費後預估到帳數量(目標 token 最小單位字串),下單時 main_channel_estimated_out_amount 直接使用此值
estimated_out_usd預估到帳金額對應 USD(小數字串,僅展示)
channel_fee實際生效費率(百分比),命中 promo 時為折後費率
channel_fee_original原始基礎費率(百分比)
promo_applied是否命中促銷
discount_pct促銷折扣百分比,未命中時為 null

1.4 失敗回應(HTTP 非 200)

詢價還沒開始建立 SSE 串流時的錯誤用一般 JSON 包絡回傳:

HTTP情境
400缺少必填參數(src_token_id/dst_token_id required 等)
400token not found / chain not found / channel not found
400no available channels
502quote service timed out / quote service unavailable

1.5 範例

請求:

curl -N \
"https://<host>/api/v2/quote/stream?src_token_id=1&dst_token_id=2&amount_in=1000000&slippage=0.01"

回應(節選):

event: channel
data: {"channel_id":101,"name":"Channel A","symbol":"USDT","channel_icon":"https://.../a.png","estimated_out_str":"999500000","estimated_out_usd":"999.500000","channel_fee":0.3,"channel_fee_original":0.3,"promo_applied":false,"discount_pct":null}

event: channel
data: {"channel_id":102,"name":"Channel B","symbol":"USDT","channel_icon":"https://.../b.png","estimated_out_str":"999200000","estimated_out_usd":"999.200000","channel_fee":0.3,"channel_fee_original":0.3,"promo_applied":false,"discount_pct":null}

event: error
data: {"channel_id":103,"msg":"upstream timeout"}

event: done
data: {}

1.6 瀏覽器 / Node.js 用戶端

EventSource(瀏覽器)

const url = new URL('https://<host>/api/v2/quote/stream');
url.searchParams.set('src_token_id', '1');
url.searchParams.set('dst_token_id', '2');
url.searchParams.set('amount_in', '1000000');
url.searchParams.set('slippage', '0.01');

const es = new EventSource(url.toString());

const channels = [];

es.addEventListener('channel', (event) => {
channels.push(JSON.parse(event.data));
});

es.addEventListener('error', (event) => {
// 單通道錯誤:data 可解析;網路層錯誤:data 可能為空。
console.warn('channel error', event.data);
});

es.addEventListener('done', () => {
es.close();
const best = channels.reduce((a, b) =>
BigInt(b.estimated_out_str) > BigInt(a.estimated_out_str) ? b : a
);
console.log('best quote', best);
});

Fetch + Streams(Node.js / Deno)

const res = await fetch(url, { headers: { Accept: 'text/event-stream' } });
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });

let idx;
while ((idx = buffer.indexOf('\n\n')) >= 0) {
const block = buffer.slice(0, idx);
buffer = buffer.slice(idx + 2);

let event = 'message';
let data = '';
for (const line of block.split('\n')) {
if (line.startsWith('event:')) event = line.slice(6).trim();
else if (line.startsWith('data:')) data = line.slice(5).trim();
}
if (event === 'channel') handleQuote(JSON.parse(data));
if (event === 'done') return;
}
}

1.7 注意事項

  • 服務端預設上游逾時 60s,逾時後下發 event: error,再發 event: done 關閉。
  • 同一詢價請求建議只發起一次連線;若需更新報價,先關閉舊連線再建立新連線,避免資料混淆。
  • estimated_out_str扣完通道費 / 平台費 / 促銷折扣後的「淨到手」數量,可直接展示給使用者。

2. 集成商下單:POST /api/v1/integrator/orders

2.1 概述

在選定詢價結果後,集成商以服務端身分呼叫該介面建立訂單。請求體與公開的 POST /api/v1/orders 完全一致,額外透過 X-Integrator-Api-Key 頭識別集成商身分並寫入分佣上下文。

下單成功後服務端會回傳充值地址(deposit_address過期時間(expire_at。使用者在 expire_at 之前向 deposit_address 轉入 amount_in 數量的來源 token,訂單會被自動執行並跨鏈送達 dst_address

2.2 鑑權

請求需攜帶集成商 API Key,兩種寫法擇一

X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111

或:

Authorization: ApiKey aaaabbbbccccddddeeeeffff00001111

降級行為:如果未傳 API Key、Key 不存在 / 已停用,或對應集成商被停用,介面不會回傳 401/403,而是降級為一般訂單建立(不綁定集成商,無分佣)。這樣可以避免誤刪 / 誤改 Key 導致下單完全中斷。集成方上線後請關注「訂單詳情中 integrator_id 是否非空」來確認分佣鏈路是否正常。

2.3 請求

POST /api/v1/integrator/orders HTTP/1.1
Content-Type: application/json
X-Integrator-Api-Key: <api_key>

請求體:

{
"src_token_id": 1,
"dst_token_id": 2,
"amount_in": "1000000",
"dst_address": "0xRecipientAddress...",
"chosen_channel_id": 101,
"slippage": 0.01,
"main_channel_estimated_out_amount": "999500000",
"min_acceptable_out_amount": "989505000",
"channels": [
{
"channel_id": 101,
"estimated_out_str": "999500000",
"name": "Channel A",
"symbol": "USDT",
"channel_icon": "https://.../a.png",
"estimated_out_usd": "999.500000",
"channel_fee": 0.3
},
{
"channel_id": 102,
"estimated_out_str": "999200000",
"name": "Channel B"
}
],
"client_order_id": "your-internal-order-no-001",
"promo_code": "WELCOME"
}

2.3.1 必填欄位

欄位類型說明
src_token_idint來源 token id(與詢價一致)
dst_token_idint目標 token id
amount_instring來源 token 數量(最小單位字串)
dst_addressstring使用者收款地址,需與目標鏈格式匹配(EVM / Solana / Tron / UTXO 等)
chosen_channel_idint使用者選定的通道 ID,來自詢價結果的 channel_id
slippagenumber滑點,與詢價時保持一致
main_channel_estimated_out_amountstring選定通道的預估到帳,直接傳詢價結果的 estimated_out_str
min_acceptable_out_amountstring使用者能接受的最低到帳數量(最小單位字串)。通常 = main_channel_estimated_out_amount × (1 - slippage)
channelsarray本次詢價拿到的全部通道列表(用於服務端二次校驗和稽核)

channels[] 元素結構(與詢價結果一一對應):

欄位類型必填說明
channel_idintYes通道 ID
estimated_out_strstringYes該通道預估到帳
name / symbol / channel_icon / estimated_out_usd / channel_fee-No詢價結果對應欄位,可全量原樣回傳

2.3.2 可選欄位

欄位類型說明
client_order_idstring集成商側業務訂單號;同一集成商下唯一,觸發冪等回傳(同 ID 重複下單只建立一次)
client_idstring業務方自訂標識,僅日誌 / 風控用
partner_app_idint合作夥伴 app id(如有)
user_idstring集成商側終端使用者 ID(僅用於稽核與風控,不參與簽名)
invite_codestring邀請碼(如有)
promo_codestring平台促銷碼,與詢價時保持一致
is_use_walletboolean是否走集成商內部錢包流程(預設 false)
fee_ratenumber僅在合約允許時下發;通常不要傳,會按 API Key 內嵌的費率結算
min_quote_amountstring最小詢價金額校驗(保留欄位)

2.4 回應

成功 200 OK

{
"code": 0,
"msg": "ok",
"data": {
"order_id": 1234567,
"order_no": "OD2026052510000001",
"deposit_address": "0xDepositAddressGeneratedByPlatform",
"expire_at": "2026-05-25T10:30:00Z",
"fee_platform": "0",
"fee_partner": "0",
"fee_integrator": "3000",
"fee_platform_original": "3000",
"platform_fee_rate": "0",
"partner_fee_rate": "0",
"integrator_fee_rate": "0.0100"
}
}

欄位說明

欄位類型說明
order_idint平台訂單 ID
order_nostring平台訂單號,建議落庫
deposit_addressstring充值地址,把 amount_in 數量的來源 token 轉入此地址即觸發跨鏈
expire_atstringISO8601 UTC,過期前必須完成充值,否則訂單會被取消
fee_platformstring平台費快照(來源 token 最小單位字串
fee_partnerstring合作夥伴費(集成商訂單固定為 "0"
fee_integratorstring集成商抽佣金額,即本筆訂單將分給該集成商的金額
fee_platform_originalstring通道總費用(未扣集成商分成)
platform_fee_rate / partner_fee_rate / integrator_fee_ratestring對應費率快照(小數字串,例如 "0.0100" 表示 1%)

冪等情境:當本次請求的 client_order_id 已經存在並匹配上既有訂單時,會回傳僅包含 order_id / order_no / deposit_address / expire_at 的簡化回應;如需完整費用欄位,請呼叫訂單詳情介面。

2.5 錯誤回應

集成商下單完全複用一般 POST /api/v1/orders 的校驗邏輯,常見錯誤:

HTTPcodemsg說明
400400invalid request bodyJSON 解析失敗
400400src_token_id/dst_token_id required必填欄位缺失
400400token not found / chain not found內部 ID 不存在
400400dst_address invalid目標地址格式不符合目標鏈
400400chosen_channel_id not in channelschosen_channel_id 不在 channels[]
400400slippage out of range滑點不在 (0, 1)
400400min_acceptable_out_amount too low低於平台最小到帳約束
400400quote amount mismatch服務端二次詢價後偏差超閾值(請重新整理詢價後重試)
400400amount_in below minimum低於最小下單金額
400400duplicate client_order_idclient_order_id 已有不同訂單
400400split orders are not supported for integrator orders集成商訂單僅支援單通道,不接受拆單(傳入 split_count≥2is_risk=true 時報此錯)

2.5.1 單通道說明

集成商下單僅支援單通道,不接受拆單 / 多通道模式:

  • 請求中不要split_count,也不要把 is_risk 設為 true;一旦傳入 split_count ≥ 2is_risk=true,介面會回傳 split orders are not supported for integrator orders
  • 訂單按 chosen_channel_id 選定的單一通道執行,main_channel_estimated_out_amount / min_acceptable_out_amount 直接沿用詢價結果即可。
  • 費用語義:fee_platform_original 為通道總費用(未扣集成商分成);fee_platform 為扣除集成商分成後實際入帳平台的金額(集成商訂單中可能 = 0);fee_integrator 為本筆訂單分給集成商的金額。

2.6 範例

curl -X POST "https://<host>/api/v1/integrator/orders" \
-H "Content-Type: application/json" \
-H "X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111" \
-d '{
"src_token_id": 1,
"dst_token_id": 2,
"amount_in": "1000000",
"dst_address": "0xRecipient...",
"chosen_channel_id": 101,
"slippage": 0.01,
"main_channel_estimated_out_amount": "999500000",
"min_acceptable_out_amount": "989505000",
"channels": [
{ "channel_id": 101, "estimated_out_str": "999500000" },
{ "channel_id": 102, "estimated_out_str": "999200000" }
],
"client_order_id": "biz-order-20260525-001"
}'

3. 集成商退款:POST /api/v1/integrator/orders/:orderNo/request_refund

3.1 概述

當集成商訂單進入可退款狀態(充值已過期未執行、跨鏈失敗、滑點失敗等)時,集成商可以服務端身分呼叫該介面,為自己參與的訂單指定退款地址並發起退款。該介面與公開的 POST /api/v1/orders/:orderNo/request_refund 流程一致,僅多了兩點:

  1. 必須用集成商 API Key 鑑權,且只能退本集成商自己的訂單;
  2. 退款只在訂單未成功時退回本金,分成只在 swap 成功時結算:集成商訂單為單通道,swap 成功即正常計分成且不可退款;swap 失敗 / 充值過期則全額退回本金、不產生分成(詳見 §3.6)。

3.2 鑑權

該退款介面僅支援 X-Integrator-Api-Key 頭(不支援 Authorization: ApiKey 寫法):

X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111

與下單介面不同,退款介面不會降級:缺少 / 無效 API Key 直接回傳 401;集成商被停用回傳 403;訂單不屬於該集成商回傳 403

3.3 請求

POST /api/v1/integrator/orders/{orderNo}/request_refund HTTP/1.1
Content-Type: application/json
X-Integrator-Api-Key: <api_key>
參數位置類型必填說明
orderNopathstringYes下單時回傳的 order_no
refund_addressbodystringYes退款接收地址,需與來源鏈地址格式匹配

請求體:

{
"refund_address": "0xSourceChainRefundAddress..."
}

3.4 可退款狀態與處理方式

服務端會根據訂單目前狀態決定退款處理方式:

訂單狀態處理方式前置條件
Depositing / CancelledNoDeposit(充值過期)進入人工覆核ManualReview必須已過 expire_at 且未過 auto_cancel_at
RefundAddressPending / ChainFailed(On-chain Failed) / SlippageFailed(Swap Failed)失敗後自動退回本金(扣 gas)必須已記錄執行明細,否則需人工確認

已終態的訂單(Completed / Refunded / CancelledNoDeposit 已結清 / FailedNoRefund)不可再退。

3.5 回應

成功 200 OK

{
"code": 0,
"msg": "ok",
"data": "ok"
}

冪等:若該訂單已存在退款記錄且 refund_address 與之前一致,重複呼叫直接回傳成功;若傳入的 refund_address 與已有退款記錄不一致,回傳 refund_address_cannot_be_changed(退款地址不可變更)。

3.6 退款後的分成處理

集成商訂單為單通道,分成只在訂單結算且 swap 成功時寫入 integrator_commission_ledger(金額 = fee_integrator)。因此:

  • swap 成功:訂單為終態、不可退款,集成商正常拿到 fee_integrator 分成。
  • swap 失敗 / 充值過期(無成功結算):結算階段不寫入分成,全額退回本金,集成商不產生分成。
  • 退款流程不會回滾或改寫已結算的分成(包括已進入提現流程的 WithdrawRequested / Withdrawn)。

3.7 錯誤回應

HTTPmsg說明
401unauthorized缺少 / 無效 API Key
403integrator disabled集成商被停用
403order does not belong to integrator該訂單不是本集成商參與的訂單
400order no required路徑缺少 orderNo
400refund_address required缺少退款地址
400order not found訂單不存在
400order_already_finalized訂單已終態,不可退
400order_not_in_refundable_state目前狀態不支援退款
400order_not_expired_yet充值尚未過期,暫不能退
400order_auto_cancel_window_passed已過自動取消視窗
400refund_requires_manual_confirmation無執行明細記錄,需人工確認
400no_amount_to_refund無可退金額
400refund_address_cannot_be_changed已有退款記錄且地址不一致
400order_state_changed_retry狀態被併發修改,請重試

3.8 範例

curl -X POST "https://<host>/api/v1/integrator/orders/OD2026052510000001/request_refund" \
-H "Content-Type: application/json" \
-H "X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111" \
-d '{
"refund_address": "0xSourceChainRefundAddress..."
}'

4. 端到端流程

┌──────────────────┐
│ 集成商服務端 │
└────────┬─────────┘
│ ① GET /api/v2/quote/stream (SSE)
│ src_token_id / dst_token_id / amount_in / slippage

┌──────────────────┐
│ Any2Any API │ ──► event: channel × N (即時推送)
└────────┬─────────┘ event: done

│ ② 在收到的通道中選出最優 quote (estimated_out_str 最大)
│ 計算 min_acceptable_out_amount = floor(estimated_out_str * (1 - slippage))

│ ③ POST /api/v1/integrator/orders
│ X-Integrator-Api-Key: <api_key>
│ body: { ..., chosen_channel_id, channels[], main_channel_estimated_out_amount, min_acceptable_out_amount }

┌──────────────────┐
│ Any2Any API │ ──► { order_no, deposit_address, expire_at, fee_integrator, ... }
└────────┬─────────┘

│ ④ 把 deposit_address 展示給終端使用者 / 由集成商錢包轉帳

│ ⑤ 使用者在 expire_at 之前完成充值

┌────────────┐
│ 鏈上結算 │ ──► 自動跨鏈 → 使用者的 dst_address
└────────────┘

│ ⑥ 訂單終態後,集成商抽佣自動進入集成商入口「可提現餘額」

Integrator Portal → 提現

5. 常見問題(FAQ)

Q1:詢價的 estimated_out_str 已經扣過通道費了,下單時還要再扣一次嗎?

不會。estimated_out_str對外展示口徑,已經包含通道費 / 平台費 / 促銷折扣的淨到手金額;min_acceptable_out_amount 直接基於它乘以 (1 - slippage) 即可。

Q2:channels[] 必須傳全部詢價結果嗎?

建議傳整次詢價收到的全部 channel 事件對應的物件,至少要包含 chosen_channel_id 對應的那一條。服務端會用它來做滑點 / 通道選擇的二次校驗,少傳可能觸發 chosen_channel_id not in channels

Q3:API Key 外洩怎麼辦?

立即在集成商入口裡刪除該 Key(同時新建一個新的 Key),刪除後立即失效。已經建立的訂單不受影響(費率快照已落表)。

Q4:是否需要每次都重新詢價?

是。estimated_out_str 是基於上游即時報價的,下單時會做二次詢價 + 偏差校驗,超出閾值會報 quote amount mismatch。建議詢價 → 下單間隔不超過 10s

Q5:下單後多久過期?

expire_at 欄位決定(通常 15~30 分鐘),過期前必須充值,否則訂單被取消,分佣不會產生。

Q6:抽佣金額如何到帳?

訂單終態(Settled)後,對應 fee_integrator 金額會按訂單建立時的來源幣價格快照,直接進入集成商的「可提現餘額(Available)」,可在集成商入口發起提現。


6. 相關連結

  • 集成商入口、API Key 管理、佣金 / 提現等完整說明:Integrator API Integration Guide
  • Postman 集合:aggregator-api.postman_collection.json 中的 Quotes / Orders / Integrator Portal (Public) 分組