快速接入

如果你只是想先把接入跑通,先看这一页就够了:先生成签名请求头,再调用创建收款订单接口,最后在回调地址里验签并返回 2xx。

下面示例默认使用创建收款订单;创建代付订单时,签名方式、请求头和回调头完全相同,只需要把接口地址和业务参数替换掉。

正式 API 地址https://api.uugate.com
支持币种TRC20-USDT · BEP20-USDT · BEP20-USDC · SPL-USDC
回调准备API Key + notifyUrl
✓Merchant UID 是平台分配给商户的唯一身份标识,请求头 x-merchant-uid 和回调通知都会使用到它。
✓准备商户后台 API 接入中的 mch_ 开头 API Key。
✓准备一个商户自己可公网访问的 notifyUrl,下单时必须传入。
Step 1

生成签名请求头

开放接口请求统一使用 x-api-key、x-merchant-uid、x-timestamp、x-nonce、x-signature 这 5 个请求头。

  • x-api-key 和签名密钥都使用同一串 mch_ 开头 API Key。
  • 签名 path 只写接口路径,例如 /openapi/payin/orders,不要带域名。
  • 签名原文固定为 Merchant UID、timestamp、nonce、method、path、canonical query、canonical body。
Node.js
import { createHmac, randomBytes } from 'node:crypto';

function required(name) {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(name + ' is required');
  return value;
}
const uid = "880001", key = "mch_xxxxxxxxxxxxxxxxxxxx";
const timestamp = "1776193200000", nonce = "2f5c7b147c3748f0a8b3d9bb38aa91a4";
const method = 'POST', path = '/openapi/payin/orders';
const canonicalQuery = '', canonicalBody = "{\"amount\":\"100.00\",\"chainCode\":\"TRON\",\"merchantOrderNo\":\"M202604150001\",\"notifyUrl\":\"https://merchant.example.com/api/uugate/payin-notify\",\"tokenSymbol\":\"USDT\"}";
const payload = [uid, timestamp, nonce, method, path, canonicalQuery, canonicalBody].join('\n');
const signature = createHmac('sha256', key).update(payload, 'utf8').digest('hex');
console.log(signature);
Step 2

创建收款订单

签名头准备好后,直接调用创建收款订单接口;成功后重点拿到 orderNo、cashierUrl、paymentUri 三个字段。

  • 接口地址:POST /openapi/payin/orders。
  • 商户前端如果要跳转收银台,直接使用返回的 cashierUrl。
  • notifyUrl 为必填字段,直接跟下单请求一起传,不需要再去后台单独配置回调地址。
Node.js
import { createHmac, randomBytes } from 'node:crypto';

function required(name) {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(name + ' is required');
  return value;
}
const uid = required("UUGATE_MERCHANT_UID"), key = required("UUGATE_API_KEY");
const timestamp = String(Date.now()), nonce = randomBytes(16).toString("hex");
const method = 'POST', path = '/openapi/payin/orders';
const canonicalQuery = '', canonicalBody = "{\"amount\":\"100.00\",\"chainCode\":\"TRON\",\"merchantOrderNo\":\"M202604150001\",\"notifyUrl\":\"https://merchant.example.com/api/uugate/payin-notify\",\"tokenSymbol\":\"USDT\"}";
const payload = [uid, timestamp, nonce, method, path, canonicalQuery, canonicalBody].join('\n');
const signature = createHmac('sha256', key).update(payload, 'utf8').digest('hex');
const base = (process.env.UUGATE_BASE_URL || 'https://api.uugate.com').replace(/\/+$/, '');
const response = await fetch(base + path, {
  method, redirect: 'error', signal: AbortSignal.timeout(30000),
  headers: { 'Content-Type': 'application/json', 'x-api-key': key, 'x-merchant-uid': uid,
    'x-timestamp': timestamp, 'x-nonce': nonce, 'x-signature': signature },
  ...(canonicalBody ? { body: canonicalBody } : {}),
});
const raw = await response.text();
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + (raw || 'empty response body'));
JSON.parse(raw);
console.log(raw);
Step 3

接收并处理回调

平台回调到你下单时传入的 notifyUrl 后,先用 apiKey 验签,验签通过再更新业务订单,最后返回 200 或其他 2xx。

  • 回调验签使用 apiKey,不使用开放接口的 x-signature。
  • 收款场景建议把 status=completed 视为最终成功。
  • 以下代码只演示回调验签:将原始请求体作为标准输入,回调头映射到 UUGATE_CALLBACK_TIMESTAMP、UUGATE_CALLBACK_NONCE、UUGATE_CALLBACK_SIGNATURE,UUGATE_API_KEY 使用创建该订单时的 API Key。验签后仍须核对订单并事务幂等持久化,完成后才返回 HTTP 2xx。
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

function required(name) {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(name + ' is required');
  return value;
}
const key = required('UUGATE_API_KEY');
const timestamp = required('UUGATE_CALLBACK_TIMESTAMP'), nonce = required('UUGATE_CALLBACK_NONCE');
const signature = required('UUGATE_CALLBACK_SIGNATURE');
const chunks = [];
for await (const chunk of process.stdin) chunks.push(chunk);
const rawBody = Buffer.concat(chunks);
const expected = createHmac('sha256', key).update(timestamp + '.' + nonce + '.', 'utf8').update(rawBody).digest();
const actual = Buffer.from(signature, 'hex');
if (!/^[a-fA-F0-9]{64}$/.test(signature) || actual.length !== expected.length || !timingSafeEqual(expected, actual))
  throw new Error('Invalid callback signature');
console.log('VERIFIED');

下面的固定时间戳、nonce 和凭据仅为离线签名对照值,不可用于实际请求。正式请求使用当前毫秒时间戳和新的随机 nonce。接口路径没有版本前缀;v1 仅指回调签名版本。

运行:Java 保存为 UUGateExample.java(回调为 UUGateCallback.java),使用 java 文件名;PHP 保存为 example.php,使用 php example.php;Go 保存为 example.go,使用 go run example.go;Node.js 保存为 example.mjs,使用 node example.mjs。回调原文从标准输入读取。

收款和代付共用同一套签名算法与回调头,不需要维护两套鉴权逻辑。

如果你已经能看懂这一页,继续往下看详细接口参数、返回结构和 SDK 下载即可。