Quick Start

If you just want to complete the first integration, this page is enough: generate signed headers, create a pay-in order, then verify the callback and return a 2xx response.

The examples use pay-in order creation by default. Payout orders use the same signature, request headers, and callback headers; only the endpoint and business payload change.

Production APIhttps://api.uugate.com
Supported assetsTRC20-USDT · BEP20-USDT · BEP20-USDC · SPL-USDC
Callback setupAPI Key + notifyUrl
✓Merchant UID is the unique merchant identity assigned by the platform. It is used in x-merchant-uid and callback payloads.
✓Prepare the API Key beginning with mch_ from the merchant API settings.
✓Prepare a publicly reachable notifyUrl owned by the merchant; it is required when creating orders.
Step 1

Generate Signed Headers

OpenAPI requests use five headers: x-api-key, x-merchant-uid, x-timestamp, x-nonce, and x-signature.

  • Use the same mch_ API Key as both x-api-key and the signing secret.
  • The signature path must be the API path only, for example /openapi/payin/orders, without the domain.
  • The signature payload is Merchant UID, timestamp, nonce, method, path, canonical query, and 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

Create Pay-in Order

After preparing the signed headers, call the create pay-in order API. On success, use orderNo, cashierUrl, and paymentUri.

  • Endpoint: POST /openapi/payin/orders.
  • Use the returned cashierUrl when the merchant frontend needs to redirect to checkout.
  • notifyUrl is required and is passed with the order request. No separate backend callback configuration is needed.
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

Receive and Process Callback

When the platform calls your notifyUrl, verify it with apiKey first, update the business order after verification, then return 200 or another 2xx response.

  • Callback verification uses apiKey, not the OpenAPI x-signature.
  • For pay-in, treat status=completed as the final success state.
  • This code only verifies callbacks. Pass the raw request body to standard input and map callback headers to UUGATE_CALLBACK_TIMESTAMP, UUGATE_CALLBACK_NONCE and UUGATE_CALLBACK_SIGNATURE. UUGATE_API_KEY must be the key used to create the order. Then validate the order and persist it idempotently in a transaction before returning 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');

The fixed timestamp, nonce and credentials below are offline signature test values. Use the current millisecond timestamp and a new random nonce for real requests. API paths have no version prefix; v1 refers only to callback signing.

Run: save Java as UUGateExample.java (UUGateCallback.java for callbacks) and use java filename; PHP: php example.php; Go: go run example.go; Node.js: node example.mjs. Callback verifiers read the raw body from standard input.

Pay-in and payout share the same signing algorithm and callback headers, so you do not need two authentication flows.

Once this page makes sense, continue down for detailed parameters, response structures, and SDK downloads.