Callback d’encaissement

The merchant server is called only after a pay-in completes successfully; the callback status is always completed.

Only final payment results are sent. Any HTTP 2xx response stops delivery immediately. After the first failure, the platform retries in 1 minute; after another failure, it makes a final retry in 5 minutes, for up to 3 attempts total. Callback business times use Beijing time in YYYY/MM/DD HH:mm:ss format.

POSTnotifyUrl de la demande de création d'ordre de paiement body

En-têtes

ChampTypeObligatoireDescription
content-typeapplication/jsonOuiToujours application/json.
x-callback-timestampstringOuiRappel timestamp en millisecondes.
x-callback-noncestringOuiRappel nonce.
x-callback-signaturestringOuiHMAC-SHA256(apiKey, timestamp + "." + nonce + "." + rawBody)。
x-callback-signature-algorithmstringOuiToujours HMAC-SHA256.
x-callback-signature-versionstringOuiToujours v1.

Paramètres

ChampTypeObligatoireDescription
merchantUidstringOuiMerchant UID.
orderNostringOuiNuméro de commande de la plateforme.
merchantOrderNostring | nullOuiNuméro de commande du commerçant.
bindKeystring | nullOuiClé de liaison pour la recharge des membres. Généralement vide pour les ordres de paiement partagés.
chainCodestring | nullOuiRéseau de la commande.
tokenSymbolstring | nullOuiDevise de la commande.
txHashstring | nullOuiHash de la transaction associée ; null avant détection.
expectedAmountstring | nullOuiMontant de la commande.
paidAmountstringOuiMontant réel payé.
fromAddressstring | nullOuiAdresse de l'expéditeur.
statusstringOuiFinal successful pay-in status; always completed.
modestringOuiMode : shared ou exclusive.
businessTypestringOuiType métier de la commande.
serviceFeeUsdstringNonFrais de service de cet encaissement.
feeBalanceAfterUsdstringNonSolde du compte de frais après facturation.
payerRiskStatusstringOuiStatut d’évaluation du risque du payeur.
payerRiskLabelstring | nullOuiLibellé d’évaluation du risque du payeur.

Exemple de requête

{
  "merchantUid": "880001",
  "orderNo": "PI1776193200123ABCD1234",
  "merchantOrderNo": "M202604150001",
  "bindKey": null,
  "expectedAmount": "100.000000",
  "paidAmount": "100.000000",
  "chainCode": "TRON",
  "tokenSymbol": "USDT",
  "txHash": "f7f17891f52c35d0c93170f0d12bb347ac16ab34853b4bc8dcf7fd0a8c9aa321",
  "fromAddress": "TS7b7iD8G2PaPqK1TqSmLJ9nrrYH4oKX1S",
  "status": "completed",
  "mode": "shared",
  "businessType": "fixed",
  "serviceFeeUsd": "0.000000",
  "feeBalanceAfterUsd": "10.000000",
  "payerRiskStatus": "low_risk",
  "payerRiskLabel": "低风险"
}

Champs de réponse

ChampTypeObligatoireDescription
HTTP Status200-299OuiToute réponse 2xx est considérée comme un succès.
Response Bodystring | jsonNonLa réponse body peut être personnalisée.

Exemple de réponse

HTTP/1.1 200 OK
Content-Type: application/json

{
  "ok": true
}

Exemples de code

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

Notes

  • La vérification du rappel utilise apiKey, et non l'en-tête de demande de signature x OpenAPI.
  • Après vérification, contrôlez marchand, commande, actif, réseau et montant. Enregistrez paiement et résultat dans une transaction idempotente par commande avant de répondre 2xx ; aucun double crédit ou livraison.
  • Pour le rechargement des membres, utilisez bindKey directement comme identifiant de membre afin de ne pas avoir besoin d'une table de mappage orderNo supplémentaire vers le membre.
  • Ce code vérifie uniquement les callbacks. Transmettez le corps brut à l’entrée standard et mappez les en-têtes vers UUGATE_CALLBACK_TIMESTAMP, UUGATE_CALLBACK_NONCE et UUGATE_CALLBACK_SIGNATURE. UUGATE_API_KEY doit être la clé utilisée pour créer la commande. Validez ensuite la commande et persistez-la de façon idempotente dans une transaction avant de renvoyer HTTP 2xx.