AIO Merchant Integration Manual (pay-in vs pay-out)
AIO 商户接入手册(入金与出金对比)
This doc focuses on what integrators need to ship fast: how to build the two pay-in flavors, how to build pay-outs, and how to read callbacks. Field names and statuses mirror the code.
本文只讲接入方最需要的内容,帮你快速上线:两种入金(pay-in)、出金(pay-out)的请求怎么做,回调怎么读。字段名和状态值与代码一致。
Quick map
快速导航
Base path:
/v2
基础路径:/v2Create:
POST /v2/tx/pay-in,POST /v2/tx/pay-in-longtime,POST /v2/tx/pay-out
创建:POST /v2/tx/pay-in、POST /v2/tx/pay-in-longtime、POST /v2/tx/pay-outRead:
GET /v2/tx/info/{txid},GET /v2/tx,GET /v2/tx/sub-txs
查询:GET /v2/tx/info/{txid}、GET /v2/tx、GET /v2/tx/sub-txsbind_info: correlation string you set; echoed in callbacks (use it for user/order ids).
bind_info:你自己写的关联字符串,AIO 会在回调里原样带回。建议用它放 user id、order id 等。
Pay-in types at a glance
入金类型一眼看懂
| Flow | Use when | Fields that make it this type | Address life | Completion rule |
|---|---|---|---|---|
| One-time pay-in | Single purchase/invoice | vs_token + vs_amount (chain/token optional) |
~1 day after an address is issued | Tx completes when received >= expected amount; address expires at overdue_at |
| Long-time pay-in | Reusable user deposit address | vs_token + chain, without vs_amount (is_longtime_tx = true) |
Rolling 90 days since last deposit | Stays Pending; extends to 90 days on each deposit; turns Overdue after 90 days idle |
Chinese summary
中文总结:
- One-time pay-in:一次性订单入金。关键特征是 有
vs_amount。地址一般在分配后约 1 天过期(看overdue_at),金额满足后 Tx 会Completed。 - Long-time pay-in:可复用入金地址。关键特征是 没有
vs_amount,is_longtime_tx=true。Tx 通常一直Pending,每次入金会把过期时间延到未来 90 天,90 天没入金会Overdue。
Merchant pattern: request one long-time pay-in per user per chain, store your user id in bind_info, and reuse that address until it expires.
商户常用做法:每个用户、每条链各创建一个 long-time 入金,把 user id 写进 bind_info,一直复用该地址直到过期。
Build the requests
构建请求
One-time pay-in (invoice-style)
一次性入金(订单/账单)
POST /v2/tx/pay-in
Required: vs_token, vs_amount > 0 (min $0.5), chain.
Optional: token, bind_info.
必填:vs_token、vs_amount > 0(最低约 0.5 美元)、chain。
可选:token、bind_info。
Notes
注意点:
- If
tokenis missing andvs_tokenis a crypto token, the server uses that same token. If only one chain supports the token, the chain is auto-filled.
如果没传token,并且vs_token本身就是某个币种,服务端会直接用它作为token。如果该币只支持一条链,链可能会被自动补全。 - If
vs_amountis present, it is a one-time transaction.
只要有vs_amount,就属于一次性入金。
Example
示例:
{
"vs_token": "USD",
"vs_amount": "50",
"chain": "Ethereum",
"token": "USDT",
"bind_info": "order=O123;user=U456"
}
What you get back: txid, addr, pay_url, status (Pending once an address is issued), is_longtime_tx=false, overdue_at.
返回通常包含:txid、addr、pay_url、status(地址分配后一般为 Pending)、is_longtime_tx=false、overdue_at。
Long-time pay-in (reusable address)
长期入金(可复用地址)
POST /v2/tx/pay-in-longtime
Required: chain (which defines the address), vs_token (defaults to USD).
Optional: bind_info.
必填:chain(决定地址所属链)、vs_token(默认 USD)。
可选:bind_info。
Rules
规则:
- Omitting
vs_amountis what makes it a long-time pay-in transaction.
不传vs_amount,就是 long-time 入金。 - Address is locked for 90 days; every deposit resets
overdue_atto 90 days out.
地址有效期按 90 天滚动,每次入金会把overdue_at刷新为“现在 + 90 天”。 - The Tx stays
Pendingwhile active. When idle pastoverdue_atit becomesOverdueand the address should be rotated.
Tx 活跃时通常一直Pending。超过overdue_at没入金则变Overdue,需要换新地址。
Example
示例:
{
"vs_token": "USD",
"chain": "Ethereum",
"bind_info": "user=U456"
}
Pay-out (withdrawal)
出金(提现/付款)
POST /v2/tx/pay-out
Required: chain, payout_data (array length 1..30) with token, to_addr, amount > 0.
Optional: none—bind_info is not used for pay-outs.
必填:chain、payout_data(数组长度 1..30),每项包含 token、to_addr、amount > 0。
可选:无(出金不使用 bind_info)。
Example
示例:
{
"chain": "Ethereum",
"payout_data": [
{ "token": "USDT", "to_addr": "0xRecipient1", "amount": "25" },
{ "token": "USDT", "to_addr": "0xRecipient2", "amount": "10" }
]
}
Status on create: Pending Execution (auto-run) or Pending Approval (needs operator approval).
创建后的状态一般是:Pending Execution(自动执行)或 Pending Approval(需要人工审批)。
What the API returns
API 返回说明:
txid,type,status,usd,chain,created_at,overdue_at
返回会有txid、type、status、usd、chain、created_at、overdue_at- Parent-level
token,vs_token,vs_amount,amountmay be null for pay-outs—usesub_txsin callbacks to see payout items.
出金的父级 Tx 上,token、vs_token、vs_amount、amount可能是 null,具体每笔出金看回调里的sub_txs。
List transactions
列表查询交易
GET /v2/tx returns paged Tx summaries (pay-in and pay-out).
GET /v2/tx 返回分页的交易摘要(包含入金与出金)。
Query params (most used)
常用查询参数:
type: filter byPay InorPay Out.
type:按Pay In或Pay Out过滤search: for merchants, matchestxid(prefix withIfor pay-in,Ofor pay-out).
search:模糊匹配txid(入金I开头,出金O开头)status: filter by Tx status (e.g.,Pending,Completed,Overdue,Closed).
status:按状态过滤(如Pending、Completed、Overdue、Closed)chain,token: narrow to a network/token.
chain、token:按链/币过滤bind_info: filter by your correlation string.
bind_info:按你的关联字符串过滤st,et: UTC time range for creation time.
st、et:创建时间范围(UTC)page,size: pagination;sort:ASCorDESCby creation time.
page、size:分页;sort:按创建时间ASC或DESC
Response shape (fields you’ll see in data.txs[])
返回字段(常见于 data.txs[]):
txid,type,status- Amounts:
vs_token,vs_amount,usd,token,amount,actual_amount - Network:
chain,addr,pay_url(for pay-ins) - Lifecycle:
created_at,overdue_at(for pay-in,overdue_atis the address expiry) - Fees:
gas,fee,total_fee,total_fee_usd - Other:
bind_info,is_longtime_tx
Pay-in reading tips
入金阅读小技巧:
- One-time pay-in:
vs_amountset;token/amountset; completes when paid.
一次性入金:vs_amount有值;token/amount有值;满足金额后会Completed - Long-time pay-in:
vs_amountnull;is_longtime_txtrue; staysPendingwhile address is active.
长期入金:vs_amount为 null;is_longtime_tx=true;活跃期通常一直Pending
Pay-out reading tips
出金阅读小技巧:
vs_amount,token,amountmay be null at the parent Tx; check payout details via callbacks orGET /v2/tx/info/{txid}forsub_txs.
父级 Tx 上vs_amount、token、amount可能为 null,明细看回调或GET /v2/tx/info/{txid}的sub_txs。
Example (abbreviated)
示例(精简版):
{
"success": true,
"msg": "ok",
"data": {
"page": 1,
"size": 10,
"total": 1,
"max_page": 1,
"txs": [
{
"txid": "I...",
"type": "Pay In",
"status": "Pending",
"vs_token": "USD",
"vs_amount": "50",
"usd": "50",
"chain": "Ethereum",
"token": "USDT",
"amount": "50",
"actual_amount": "0",
"addr": "0x...",
"gas": "0",
"fee": "0",
"created_at": "2019-08-24T14:15:22.123Z",
"overdue_at": "2019-08-25T14:15:22.123Z",
"is_longtime_tx": false,
"total_fee": "0",
"total_fee_usd": "0",
"pay_url": "https://..."
}
]
}
}
Use GET /v2/tx/info/{txid} when you need the SubTx list for a specific transaction.
当你需要某笔交易的 SubTx 明细时,用 GET /v2/tx/info/{txid}。
Callbacks (AIO → Merchant)
回调(AIO 发给商户)
Every callback payload
每个回调的基础结构:
{ "type": "Transaction" | "Sub Transaction", "data": { ... } }
How to tell what you got
如何判断回调类型:
type="Transaction": a Tx status change (pay-in or pay-out). Idempotency key:(txid, status).
type="Transaction":交易状态变更(入金或出金)。幂等键建议用(txid, status)type="Sub Transaction": a chain transfer was seen. Idempotency key:sub_txid.
type="Sub Transaction":链上转账记录出现。幂等键建议用sub_txid- Inside
data,data.typeisPay InorPay Out.
在data里,data.type会是Pay In或Pay Out
Pay-in flavor detection
区分入金类型:
- One-time pay-in callbacks have
vs_amountpopulated.
一次性入金:vs_amount有值 - Long-time pay-in callbacks have
vs_amountnull and keep reportingPendingfor the Tx. Credit deposits fromSub Transactioncallbacks and watch forOverdueto rotate the address.
长期入金:vs_amount为 null,Tx 常一直Pending。入账以Sub Transaction回调为主,同时监听Overdue来换地址。
Pay-out callbacks
出金回调:
data.type="Pay Out",vs_amountis null, amounts live insub_txsitems.
data.type="Pay Out",vs_amount为 null,金额明细在sub_txs里。
Minimal shapes
最小示例:
// Transaction callback (example)
{
"type": "Transaction",
"data": {
"txid": "I...",
"type": "Pay In",
"status": "Pending",
"vs_token": "USD",
"vs_amount": "50",
"chain": "Ethereum",
"token": "USDT",
"amount": "50",
"bind_info": "order=O123;user=U456",
"sub_txs": []
}
}
// Sub Transaction callback (deposit or withdrawal record)
{
"type": "Sub Transaction",
"data": {
"txid": "I...",
"type": "Pay In",
"status": "Pending",
"chain": "Ethereum",
"token": "USDT",
"sub_txs": [
{
"sub_txid": "7629621714635423",
"status": "Completed",
"amount": "50",
"hash": "0x...",
"from_addr": "0x...",
"to_addr": "0x..."
}
]
}
}
// Pay-out Transaction callback (batch payout example)
{
"type": "Transaction",
"data": {
"uid": "U_xxx",
"txid": "Oxxx",
"status": "Completed",
"type": "Pay Out",
"vs_token": null,
"vs_amount": null,
"usd": "2.98",
"chain": "Ethereum",
"token": null,
"amount": null,
"bind_info": null,
"sub_txs": [
{
"sub_txid": "subtx1",
"status": "Completed",
"token": "USDT",
"amount": "1",
"hash": "0xhash1",
"from_addr": null,
"to_addr": "0xRecipient1"
},
{
"sub_txid": "subtx2",
"status": "Completed",
"token": "USDT",
"amount": "2",
"hash": "0xhash2",
"from_addr": null,
"to_addr": "0xRecipient1"
}
]
}
}
Signature verification (must-do)
签名校验(必须做):
1) Check Body-MD5 == md5(raw_body_bytes).hexdigest().
校验 Body-MD5 是否等于 raw body 的 md5 hex
2) Build origin: {Algorithm} | {Date} | POST {callback_url} | {Body-MD5} using the exact configured callback URL.
用“配置在 AIO 的完整 callback_url”组装 origin
3) Validate Aio-Sign == base64(HMAC-SHA256(secret_key, origin)).
用回调密钥算 HMAC-SHA256,再 base64,对比 Aio-Sign
Delivery semantics: callbacks retry and can arrive out of order—handlers must be idempotent.
投递语义:回调会重试,且可能乱序到达,所以处理端必须幂等。
Status guide
状态指南
Pending Execution: created but not active (e.g., pay-in without chain, pay-out queued).
Pending Execution:已创建但尚未激活(例如入金没提供 chain,或出金排队等待执行)Pending Approval: pay-out awaiting manual approval.
Pending Approval:出金等待人工审批Pending: active (pay-in address live, pay-out submitted).
Pending:进行中(入金地址可用,出金已提交)Completed: terminal success (one-time pay-in meets target; pay-out finished).
Completed:成功终态(一次性入金满足目标,出金完成)Overdue: terminal expiry (address expired; rotate to a new Tx).
Overdue:过期终态(入金地址过期,必须换新 Tx/地址)Closed: terminal cancel/close.
Closed:关闭/取消终态Suspended: internal exception—escalate.
Suspended:内部异常状态,需要升级处理(找 ops)
Auth headers (Merchant → AIO)
认证请求头(商户到 AIO)
aio-api-key,aio-sign,algorithm=HMAC-SHA256,date(ISO). Optional:aio-aes.
必要头:aio-api-key、aio-sign、algorithm=HMAC-SHA256、date(ISO 时间)。可选:aio-aesSign origin:
{algorithm} | {date} | {METHOD} {path}[?query][ | body_md5].
签名原串:{algorithm} | {date} | {METHOD} {path}[?query][ | body_md5]