Exemple de demande et vérification de signature

Méthode de requête : les API utilisent principalement `GET` / `POST`; les requêtes JSON utilisent `application/json`.

Authentification : les requêtes OpenAPI sont vérifiées avec des en-têtes et des signatures HMAC-SHA256.

En-têtes

ChampTypeObligatoireDescription
x-api-keystringOuiMarchand API Key. Utilisez la valeur commençant par mch_ de la page des paramètres API du marchand.
x-merchant-uidstringOuiMerchant UID.
x-timestampstringOuiMilliseconde à 13 chiffres timestamp. La dérive d'horloge autorisée par défaut est de 5 minutes.
x-noncestringOuiChaîne aléatoire unique pour chaque requête, jusqu'à 128 caractères. La réutilisation est rejetée.
x-signaturestringOuiSignature hexadécimale générée avec HMAC-SHA256. La charge utile contient Merchant UID, timestamp, nonce, la méthode, le chemin, le query canonique et le body canonique.

Règles de signature

  • Les requêtes utilisent application/json. Pour les GET API sans body, la partie body dans la signature est une chaîne vide.
  • Le chemin de signature doit être le chemin brut API, par exemple /openapi/payin/orders, sans le domaine.
  • Les paramètres de requête sont triés par clé ; Les clés JSON body sont triées avant la sérialisation.
  • Les échecs de signature courants incluent des timestamp expirés, des valeurs nonce répétées, un ordre de champ incohérent ou des espaces supplémentaires.

Le timestamp, le nonce et les identifiants fixes sont des valeurs de test de signature hors ligne. Pour une requête réelle, utilisez le timestamp actuel en millisecondes et un nouveau nonce aléatoire. Les chemins API n’ont pas de préfixe de version ; v1 désigne uniquement la signature des callbacks.

Exemple de payload de signature

880001
1776193200000
2f5c7b147c3748f0a8b3d9bb38aa91a4
POST
/openapi/payin/orders

{"amount":"100.00","chainCode":"TRON","merchantOrderNo":"M202604150001","notifyUrl":"https://merchant.example.com/api/uugate/payin-notify","tokenSymbol":"USDT"}

Exemple complet

POST /openapi/payin/orders HTTP/1.1
Host: api.uugate.com
Content-Type: application/json
x-api-key: mch_xxxxxxxxxxxxxxxxxxxx
x-merchant-uid: 880001
x-timestamp: 1776193200000
x-nonce: 2f5c7b147c3748f0a8b3d9bb38aa91a4
x-signature: 6d7e96fdbf3ec37c1ec515540f46c6c19b08754973c43293e54644c2da910838

{
  "chainCode": "TRON",
  "tokenSymbol": "USDT",
  "merchantOrderNo": "M202604150001",
  "amount": "100.00",
  "notifyUrl": "https://merchant.example.com/api/uugate/payin-notify"
}

Exemples de signature

  • Les quatre langages utilisent les mêmes chemins, paramètres et réponses. Les identifiants proviennent de UUGATE_MERCHANT_UID et UUGATE_API_KEY ; UUGATE_BASE_URL vaut https://api.uugate.com par défaut.
  • canonicalBody est un JSON compact aux clés triées. Après modification, triez les objets récursivement, conservez l’ordre des tableaux et utilisez UTF-8 sans échappement supplémentaire des barres obliques ou Unicode. Les montants sont des chaînes, les booléens true / false.
  • Une réponse réussie renvoie directement l’objet métier, sans enveloppe code / data. Vérifiez d’abord le statut HTTP. Les erreurs métier renvoient code, message, data et requestId ; les refus de sécurité peuvent se limiter à HTTP 401 / 403 avec un corps vide.
  • Après un timeout ou HTTP 5xx, consultez la commande avant de répéter sa création ou son envoi. Générez un nouveau timestamp, nonce et une nouvelle signature pour chaque requête.

Exécution : enregistrez Java sous UUGateExample.java (UUGateCallback.java pour les callbacks), puis utilisez java nom du fichier ; PHP : php example.php ; Go : go run example.go ; Node.js : node example.mjs. Les vérificateurs de callback lisent le corps brut sur l’entrée standard.

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);

Les erreurs métier renvoient un JSON avec code, message, data et requestId. Une clé, une signature, un timestamp ou un nonce invalide, ou une restriction de sécurité, peut renvoyer HTTP 401 / 403 avec un corps vide sans code métier. Vérifiez le statut HTTP et le corps avant de lire le JSON. Ne fondez pas vos décisions sur le texte traduit de message.

Champs d’erreur

ChampTypeObligatoireDescription
codenumberOuiCode d'erreur métier.
messagestringOuiMessage d'erreur.
dataobject | nullOuiDonnées d'erreur supplémentaires.
requestIdstring | nullOuiID de trace de demande du serveur.

Exemple d’erreur

HTTP/1.1 401 Unauthorized
Cache-Control: no-store
Content-Length: 0