USNpay API 文档

基于 RESTful 风格的加密货币收款接口。创建收款订单、查询状态、接收实时回调,资金直达您自己的钱包,固定 0.5% 透明手续费。

简介

USNpay 让您用自己的钱包收取加密货币付款。通过 API,您可以按法币定价创建订单,系统实时换算为加密金额并锁定汇率,客户付款后资金直接进入您绑定的链上钱包,平台不经手任何资金。

接入前,请先在会员中心注册商户账户并获取三项凭据:商户 ID(merchant_id)、API 公钥(api_key)、API 私钥(secret_key)。私钥仅在生成时显示一次,请妥善保管。

非托管设计:所有订单的收款地址都是您在会员中心绑定的钱包地址,付款直达您的钱包。USNpay 仅提供订单管理、汇率换算与回调通知服务。

基础信息

请求地址

所有接口以您部署的站点域名为基础,统一前缀 /v1

Base URL
https://你的域名/v1

数据格式

  • 请求体为 JSON,请设置 Content-Type: application/json
  • 响应统一为 JSON,包含 codemessagedata 三个字段
  • 金额字段均为字符串,加密金额最高 18 位小数,请用高精度库处理,切勿用浮点数
  • 时间字段为 UTC 时间

统一响应结构

JSON
{
  "code": 0,          // 0 = 成功,非 0 = 失败
  "message": "ok",
  "data": { ... }   // 业务数据,失败时为 null
}

鉴权与签名

每个 API 请求都需通过 HMAC-SHA256 签名鉴权。请在请求头中携带以下四个字段:

请求头说明
X-Merchant-Id商户 ID,如 M88021
X-Api-KeyAPI 公钥,如 pk_live_xxx
X-Timestamp当前 Unix 时间戳(秒),有效窗口 ±300 秒,用于防重放
X-Signature签名值,计算方法见下

签名计算

将以下四部分用换行符 \n 拼接为待签名串,再用 API 私钥(secret_key)做 HMAC-SHA256,输出十六进制小写字符串:

签名串结构
HTTP方法(大写) + "\n" + 请求路径 + "\n" + 时间戳 + "\n" + 请求体
                                                              ↓
              signature = HMAC_SHA256(待签名串, secret_key)
请求路径指 /v1/orders 这样的 path 部分,不含域名和查询参数。GET 请求的请求体为空字符串。时间戳必须与服务端时间相差在 5 分钟内。

签名示例

cURL / Bash
PHP
Python
Node.js
Bash
MERCHANT="M88021"
APIKEY="pk_live_xxx"
SECRET="sk_live_xxx"
TS=$(date +%s)
PATH_="/v1/orders"
BODY='{"amount":"1288.00","fiat":"CNY","asset":"USDT","network":"TRC20"}'

# 拼接待签名串并计算 HMAC
PAYLOAD="POST\n${PATH_}\n${TS}\n${BODY}"
SIG=$(printf "%b" "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')

curl -X POST https://你的域名$PATH_ \
  -H "X-Merchant-Id: $MERCHANT" \
  -H "X-Api-Key: $APIKEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d "$BODY"
PHP
function cpRequest($method, $path, $body, $cfg) {
    $ts = time();
    $json = $body ? json_encode($body, JSON_UNESCAPED_SLASHES) : '';
    $payload = strtoupper($method) . "\n" . $path . "\n" . $ts . "\n" . $json;
    $sig = hash_hmac('sha256', $payload, $cfg['secret']);

    $ch = curl_init($cfg['base'] . $path);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_POSTFIELDS    => $json,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            "X-Merchant-Id: " . $cfg['merchant'],
            "X-Api-Key: " . $cfg['apikey'],
            "X-Timestamp: " . $ts,
            "X-Signature: " . $sig,
            "Content-Type: application/json",
        ],
    ]);
    return json_decode(curl_exec($ch), true);
}
Python
import time, json, hmac, hashlib, requests

def cp_request(method, path, body, cfg):
    ts = str(int(time.time()))
    raw = json.dumps(body, separators=(',', ':')) if body else ''
    payload = f"{method.upper()}\n{path}\n{ts}\n{raw}"
    sig = hmac.new(
        cfg['secret'].encode(), payload.encode(), hashlib.sha256
    ).hexdigest()

    headers = {
        'X-Merchant-Id': cfg['merchant'],
        'X-Api-Key': cfg['apikey'],
        'X-Timestamp': ts,
        'X-Signature': sig,
        'Content-Type': 'application/json',
    }
    return requests.request(method, cfg['base'] + path, headers=headers, data=raw).json()
Node.js
const crypto = require('crypto');

async function cpRequest(method, path, body, cfg) {
  const ts = String(Math.floor(Date.now() / 1000));
  const raw = body ? JSON.stringify(body) : '';
  const payload = `${method.toUpperCase()}\n${path}\n${ts}\n${raw}`;
  const sig = crypto.createHmac('sha256', cfg.secret)
    .update(payload).digest('hex');

  const res = await fetch(cfg.base + path, {
    method,
    headers: {
      'X-Merchant-Id': cfg.merchant,
      'X-Api-Key': cfg.apikey,
      'X-Timestamp': ts,
      'X-Signature': sig,
      'Content-Type': 'application/json',
    },
    body: raw || undefined,
  });
  return res.json();
}

创建订单

POST/v1/orders

按法币金额创建一笔收款订单。系统按当前汇率换算为加密金额并锁定,返回收款地址与支付链接。

请求参数

参数类型必填说明
amountstring必填法币金额,如 "1288.00"
fiatstring可选计价法币,默认取站点配置,如 CNY/USD/EUR
assetstring必填收款币种,如 USDT/BTC/ETH
networkstring可选网络,如 TRC20/ERC20。不填则取该币种默认网络
subjectstring可选商品名称或订单备注
out_trade_nostring可选商户侧订单号,用于幂等。相同值重复请求返回同一订单
notify_urlstring可选回调通知地址。订单状态变化时推送至此
return_urlstring可选支付完成后跳转地址

请求示例

Request Body
{
  "amount": "1288.00",
  "fiat": "CNY",
  "asset": "USDT",
  "network": "TRC20",
  "subject": "会员年卡",
  "out_trade_no": "SHOP-20260528-001",
  "notify_url": "https://yourshop.com/callback"
}

响应示例

200 OK
{
  "code": 0,
  "message": "订单创建成功",
  "data": {
    "order_no": "CP-20260528-A1B2C3D4",
    "out_trade_no": "SHOP-20260528-001",
    "fiat": "CNY",
    "fiat_amount": "1288.00",
    "crypto_amount": "181.408450",
    "rate": "7.100000000000",
    "pay_address": "TJ8s...9kQz",
    "pay_url": "https://你的域名/pay/CP-20260528-A1B2C3D4",
    "status": "pending",
    "subject": "会员年卡",
    "expired_at": "2026-05-28 15:08:00",
    "created_at": "2026-05-28 14:38:00"
  }
}
创建订单前,请确保已在会员中心为对应币种绑定收款钱包地址,否则会返回"商户尚未配置收款钱包"错误。

查询订单

GET/v1/orders/{order_no}

根据平台订单号查询订单当前状态。建议优先依赖 Webhook 回调,仅在需要主动核对时调用此接口。

响应示例

200 OK
{
  "code": 0,
  "message": "ok",
  "data": {
    "order_no": "CP-20260528-A1B2C3D4",
    "status": "completed",
    "crypto_amount": "181.408450",
    "pay_address": "TJ8s...9kQz"
  }
}

订单列表

GET/v1/orders

分页查询本商户的订单列表,支持按状态筛选。

查询参数

参数说明
page页码,默认 1
size每页条数,默认 20,最大 100
status按状态筛选,可选,见订单状态表
Example
GET /v1/orders?page=1&size=20&status=completed

健康检查

GET/v1/ping

检测服务可用性,无需鉴权。返回当前服务时间。

200 OK
{ "code": 0, "data": { "time": "2026-05-28T14:38:00+00:00", "service": "USNpay" } }

Webhook 通知

当订单状态发生变化(如到账、确认、完成)时,USNpay 会向您创建订单时提供的 notify_url 发送 POST 请求。请在收到通知后返回 HTTP 2xx 状态码表示接收成功;否则系统会按指数退避策略重试,最多重试若干次(由后台配置)。

推送请求头

请求头说明
X-ChainPay-Signature推送体的 HMAC-SHA256 签名,用于验签
X-USNpay-Event事件类型,如 order.confirmed

推送内容

POST Body
{
  "order_no": "CP-20260528-A1B2C3D4",
  "out_trade_no": "SHOP-20260528-001",
  "status": "confirmed",
  "fiat": "CNY",
  "fiat_amount": "1288.00",
  "crypto_amount": "181.408450",
  "paid_amount": "181.408450",
  "tx_hash": "a7f3...d9e2",
  "timestamp": 1716885480
}

回调验签

收到回调后,请务必用您的 webhook_secret 对原始请求体重新计算 HMAC-SHA256,与请求头 X-ChainPay-Signature 比对,一致才视为合法通知。请使用恒定时间比较函数防止时序攻击。

PHP
Python
Node.js
PHP
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_CHAINPAY_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $raw, $webhookSecret);

if (!hash_equals($expected, $sig)) {
    http_response_code(401); exit('invalid signature');
}
$data = json_decode($raw, true);
// 验签通过 → 按 $data['status'] 更新本地订单
http_response_code(200); echo 'ok';
Python (Flask)
@app.route('/callback', methods=['POST'])
def callback():
    raw = request.get_data()
    sig = request.headers.get('X-ChainPay-Signature', '')
    expected = hmac.new(
        webhook_secret.encode(), raw, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return 'invalid', 401
    data = request.get_json()
    # 更新本地订单
    return 'ok', 200
Node.js (Express)
app.post('/callback', express.raw({type:'*/*'}), (req, res) => {
  const raw = req.body; // Buffer
  const sig = req.headers['x-chainpay-signature'];
  const expected = crypto.createHmac('sha256', webhookSecret)
    .update(raw).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)))
    return res.status(401).send('invalid');
  const data = JSON.parse(raw.toString());
  // 更新本地订单
  res.send('ok');
});
务必验签:未经验签的回调可能被伪造。请始终用原始请求体(未经 JSON 解析重序列化的字节)计算签名,并用恒定时间比较。同时建议以 order_no 做幂等,避免重复回调导致重复发货。

订单状态

状态含义
pending待支付,订单已创建,等待客户付款
paid已支付,检测到链上转账,确认数未达标
confirmed已确认,达到所需确认数
completed已完成,回调成功通知商户
expired已超时,超过有效期仍未支付
refunded已退款
failed失败

错误码

失败响应的 code 非 0,HTTP 状态码标识错误类别,message 含可读描述。

HTTP场景说明
401鉴权失败缺少鉴权头、签名错误、时间戳过期、API Key 无效或已吊销
403禁止访问请求 IP 不在白名单内
404资源不存在订单不存在或接口路径错误
409状态冲突订单当前状态不允许该操作
422参数错误金额无效、缺少必填参数、币种不支持
400请求错误其他业务错误,如商户未配置收款钱包
500服务异常服务端内部错误