DOCUMENTATION

Développez avec SMetaGateway.

Tout ce qu’il faut pour créer des factures USDT TRC20 depuis votre serveur, envoyer les acheteurs vers la page de paiement et confirmer les paiements par webhooks signés.

Présentation

SMetaGateway est une couche de facturation non dépositaire pour l’USDT sur TRON. Les marchands enregistrent une adresse publique de réception ; les acheteurs la paient directement. La passerelle attribue à chaque facture un montant exact unique, suit son état, enregistre les données comptables et notifie votre serveur.

Tous les endpoints se trouvent sous /api/v1, sur la même origine que ce site. Les montants sont toujours des chaînes décimales d’au plus six décimales, jamais des nombres à virgule flottante.

Flux de paiementLes données circulent entre votre serveur et la passerelle. L’argent va uniquement du portefeuille de l’acheteur au vôtre.
Votre serveur
SMetaGateway
Acheteur
Votre portefeuille TRON
  1. Votre serveur → SMetaGateway1POST /api/v1/invoices avec un Idempotency-Key
  2. SMetaGateway → Votre serveur2Facture avec payable_amount et checkout_url
  3. Votre serveur → Acheteur3Redirection de l’acheteur vers checkout_url
  4. Acheteur → SMetaGateway4L’acheteur ouvre la page de paiement et scanne le QR code
  5. Acheteur → Votre portefeuille TRON5L’acheteur envoie le montant USDT exact à votre adresse
  6. SMetaGateway6La passerelle trouve le montant exact sur la blockchain et confirme la facture
  7. SMetaGateway → Votre serveur7Webhook signé invoice.confirmed
  8. Votre serveur → SMetaGateway8GET /api/v1/invoices/{id} pour vérifier, puis honorer la commande

Flux de donnéesFlux d’argent

Démarrage rapide

1. Créez un espace et ajoutez une adresse de réception TRON. Copiez son identifiant de portefeuille.

2. Ouvrez Développeurs → Clés API et créez une clé avec une date d’expiration. Le secret commence par smg_ et n’est affiché qu’une seule fois : conservez-le en lieu sûr.

3. Créez une facture depuis votre serveur avec la requête ci-dessous. Enregistrez l’ID de la facture et le payable_amount exact.

4. Redirigez l’acheteur vers checkout_url. Ne marquez la commande comme payée qu’après un webhook invoice.confirmed vérifié et une consultation de la facture côté serveur.

Create an invoice
curl -X POST "https://smg.sabco24.com/api/v1/invoices" \
  -H "Authorization: Bearer $SMETA_API_KEY" \
  -H "Idempotency-Key: order-1001" \
  -H "Content-Type: application/json" \
  -d '{"merchant_order_id":"1001","amount":"49.95","wallet_id":"WALLET_ID","description":"Example order","expires_in_minutes":20}'

Authentification

Les intégrations serveur s’authentifient avec une clé API bearer : Authorization: Bearer smg_…. Les clés portent les portées invoices:read, invoices:write et ledger:read, et peuvent expirer après 30, 90 ou 365 jours. Une clé expirée renvoie 401 api_key_expired ; renouvelez-la dans le tableau de bord pour obtenir un nouveau secret. Gardez les clés sur votre serveur ; ne les intégrez jamais dans un navigateur ou une application mobile.

Le tableau de bord utilise un cookie de session HttpOnly. La gestion du compte, des portefeuilles, des clés API et des webhooks exige cette session propriétaire et n’est pas possible avec une clé API.

Deux modes d’authentification
Votre backend
Authorization: Bearer smg_…
Factures et export du registre
Propriétaire du tableau de bord
Cookie HttpOnly smg_session, défini par /auth/login
Compte, portefeuilles, clés API et webhooks
Authenticated request
curl "https://smg.sabco24.com/api/v1/invoices/INVOICE_ID" \
  -H "Authorization: Bearer $SMETA_API_KEY"

Montants et identification

Envoyez le prix de base sous forme de chaîne décimale, par exemple "49.95". La passerelle ajoute un ajustement positif — de 0.000001 à 0.009999 USDT sous 100 USDT, ou de 0.000001 à 0.999999 USDT à partir de 100 USDT — et renvoie base_amount, adjustment et payable_amount séparément.

Le total exact est réservé 90 jours pour le même réseau, jeton et adresse de réception, tous marchands confondus. Les totaux inutilisés sont privilégiés ; un total réutilisé place la facture en review au lieu d’une confirmation automatique.

Comment le montant à payer est construit
Prix de base49.95
Ajustement d’identification0.004217
L’acheteur paie exactement49.954217 USDT

Réservé 90 jours par réseau, jeton et adresse de réception

Amount breakdown
{
  "base_amount": "49.950000",
  "adjustment": "0.003821",
  "payable_amount": "49.953821"
}

Factures et idempotence

POST /api/v1/invoices exige l’en-tête Idempotency-Key. Rejouer la même requête avec la même clé renvoie la facture d’origine avec idempotent_replay: true ; réutiliser la clé avec d’autres données renvoie 409 idempotency_conflict.

Chaque merchant_order_id n’a qu’une facture. Enregistrez l’ID de facture et le payable_amount avec votre commande et comparez-les à l’arrivée d’un webhook.

Résultats d’idempotence
POST /invoices + Idempotency-Key
  • Nouvelle clé201 — nouvelle facture créée
  • Même clé, même corpsFacture d’origine, idempotent_replay: true
  • Même clé, corps différent409 idempotency_conflict
Response
{
  "invoice": {
    "id": "6f1c…",
    "merchant_order_id": "1001",
    "base_amount": "49.950000",
    "adjustment": "0.003821",
    "payable_amount": "49.953821",
    "status": "pending",
    "mode": "live",
    "checkout_url": "https://smg.sabco24.com/checkout/…",
    "expires_at": "2026-10-21T12:20:00.000Z",
    "reserved_until": "2027-01-19T12:00:00.000Z"
  },
  "idempotent_replay": false
}

Page de paiement et statut

checkout_url est un lien d’accès : quiconque le possède voit les informations destinées au payeur, traitez-le donc comme confidentiel. La page affiche le nom du marchand, le détail du montant, l’adresse, le QR code et l’expiration, jamais vos métadonnées ni identifiants internes.

Le statut est pending, confirmed, expired ou review. La page expire après expires_in_minutes (20 par défaut, 1440 au maximum) ; la réservation du montant dure 90 jours indépendamment.

Cycle de vie du statut de facture
Facture créée
pending
  • Montant exact reçuconfirmed
  • Délai de paiement écouléexpired
  • Montant réutilisé ou vérification manuelle requisereview

Webhooks signés

Enregistrez un endpoint HTTPS public (port 443) dans le tableau de bord. Son secret de signature n’est affiché qu’une fois. Événements : invoice.created, invoice.confirmed, invoice.expired, invoice.review et webhook.test. Les livraisons sont relancées jusqu’à huit fois et peuvent l’être manuellement.

Chaque requête porte les en-têtes X-SMeta-Event-Id, X-SMeta-Timestamp et X-SMeta-Signature. Calculez un HMAC-SHA256 sur timestamp + "." + corps brut, comparez en temps constant, rejetez les horodatages de plus de cinq minutes, puis analysez le JSON. Traitez chaque ID d’événement une seule fois.

Vérifier un webhook
  1. 1Lire le corps brut et les en-têtes X-SMeta-*
  2. 2Horodatage de moins de 5 minutes ?Rejeter (4xx)
  3. 3HMAC-SHA256(timestamp + "." + body)
  4. 4Comparaison à temps constant avec X-SMeta-SignatureRejeter (4xx)
  5. 5ID d’événement déjà traité ?Répondre 2xx, ignorer
  6. 6Mettre à jour la commande et répondre 2xx
Verify a webhook (Node.js)
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(rawBody: Buffer, headers: Record<string, string>, secret: string) {
  const timestamp = headers['x-smeta-timestamp'];
  const signature = headers['x-smeta-signature'] ?? '';
  if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return null;
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(timestamp + '.').update(rawBody).digest('hex');
  const a = Buffer.from(expected), b = Buffer.from(signature);
  if (a.length !== b.length || !timingSafeEqual(a, b)) return null;
  const event = JSON.parse(rawBody.toString('utf8'));
  return event.id === headers['x-smeta-event-id'] ? event : null;
}

Erreurs et limites de débit

Les erreurs renvoient un JSON avec error.code et error.message. Statuts courants : 400 validation, 401 authentification, 403 portée ou origine, 404 introuvable, 409 conflit ou revue, 429 limite atteinte.

La création de factures est limitée à 120 requêtes par minute et par marchand. Relancez les réponses 429 et 5xx avec un délai progressif et la même Idempotency-Key.

Error response
{
  "error": {
    "code": "idempotency_conflict",
    "message": "This idempotency key was used with different invoice details."
  },
  "mode": "live"
}

Intégrations

Le tableau de bord propose 32 recettes prêtes à copier pour Node.js, NestJS, PHP, Laravel, Python, Django, Go, Java, C#, Ruby, React, Next.js, Vue et des plateformes e-commerce comme WooCommerce, Magento et Shopware. Le plugin WooCommerce téléchargeable est une préversion qui n’accepte pas encore les clés API de production.

Les recettes sont des exemples, pas des SDK publiés. Les exemples frontend appellent votre propre backend, qui détient la clé API.

Confirmation des paiements

La passerelle lit le réseau principal TRON via TronGrid pour trouver les transferts USDT TRC20 confirmés vers chaque adresse de réception ayant une facture ouverte, et revérifie chaque transfert à l’aide d’un reçu solidifié réussi. Un transfert du payable_amount exact entre la création et l’expiration de la facture la confirme, et le hash de la transaction est enregistré dans tx_hash.

Un transfert reçu après l’expiration, correspondant à un montant déjà utilisé ou visant une facture déjà en revue fait passer le statut à review et n’est jamais confirmé automatiquement. La détection prend généralement quelques minutes. L’absence de commission et une conception non dépositaire n’établissent pas à elles seules une exonération fiscale ou une autorisation réglementaire.