DOCUMENTATION

Build with SMetaGateway.

Everything you need to create USDT TRC20 invoices from your server, send buyers to checkout and confirm payments with signed webhooks.

Overview

SMetaGateway is a noncustodial invoice layer for USDT on TRON. Merchants register a public receiving address; buyers pay that address directly. The gateway assigns each invoice a unique exact amount, tracks its status, records accounting data and notifies your server.

All API endpoints live under /api/v1 on the same origin as this website. Amounts are always decimal strings with up to six fractional digits — never floating-point numbers.

Payment flowData moves between your server and the gateway. Money moves only from the buyer’s wallet to yours.
Your server
SMetaGateway
Buyer
Your TRON wallet
  1. Your server → SMetaGateway1POST /api/v1/invoices with an Idempotency-Key
  2. SMetaGateway → Your server2Invoice with payable_amount and checkout_url
  3. Your server → Buyer3Redirect the buyer to checkout_url
  4. Buyer → SMetaGateway4Buyer opens checkout and scans the QR code
  5. Buyer → Your TRON wallet5Buyer sends the exact USDT amount to your address
  6. SMetaGateway6Gateway finds the exact amount on-chain and confirms the invoice
  7. SMetaGateway → Your server7Signed invoice.confirmed webhook
  8. Your server → SMetaGateway8GET /api/v1/invoices/{id} to verify, then fulfil the order

Data pathMoney path

Quick start

1. Create a workspace and add a TRON receiving address. Copy its wallet ID.

2. Open Developers → API keys and create a key with an expiry. The secret starts with smg_ and is shown only once, so store it securely.

3. Create an invoice from your server with the request below. Save the invoice ID and the exact payable_amount.

4. Redirect the buyer to checkout_url. Mark the order paid only after a verified invoice.confirmed webhook and a server-side invoice lookup.

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}'

Authentication

Server integrations authenticate with a bearer API key: Authorization: Bearer smg_…. Keys carry the scopes invoices:read, invoices:write and ledger:read, and can expire after 30, 90 or 365 days. An expired key returns 401 api_key_expired; roll it in the dashboard to get a new secret. Keep keys on your server; never ship them to browsers or mobile apps.

The first-party dashboard uses an HttpOnly session cookie. Account, wallet registration, API key and webhook management require that owner session and cannot be performed with an API key.

Two ways to authenticate
Your backend
Authorization: Bearer smg_…
Invoices and ledger export
Dashboard owner
HttpOnly smg_session cookie, set by /auth/login
Account, wallets, API keys and webhooks
Authenticated request
curl "https://smg.sabco24.com/api/v1/invoices/INVOICE_ID" \
  -H "Authorization: Bearer $SMETA_API_KEY"

Amounts and identification

Send the base price as a decimal string such as "49.95". The gateway adds a positive identification adjustment — 0.000001 to 0.009999 USDT below 100 USDT, or 0.000001 to 0.999999 USDT at 100 USDT and above — and returns base_amount, adjustment and payable_amount separately.

The exact payable total is reserved for 90 days for the same network, token and receiving address across all merchants. Unused totals are preferred; a reused total sets the invoice to review instead of automatic confirmation.

How the payable amount is built
Base price49.95
Identification adjustment0.004217
Buyer pays exactly49.954217 USDT

Reserved for 90 days per network, token and receiving address

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

Invoices and idempotency

POST /api/v1/invoices requires an Idempotency-Key header. Repeating the same request with the same key returns the original invoice with idempotent_replay: true; reusing a key with different details returns 409 idempotency_conflict.

Each merchant_order_id can have one invoice. Store the returned invoice ID and payable_amount with your order and compare them when a webhook arrives.

Idempotency outcomes
POST /invoices + Idempotency-Key
  • New key201 — new invoice created
  • Same key, same bodyOriginal invoice, idempotent_replay: true
  • Same key, different body409 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
}

Checkout and invoice status

checkout_url is a capability link: anyone holding it can view payer-safe details, so treat it as confidential. Checkout shows the merchant name, amount breakdown, receiving address, QR code and expiry, never your metadata or internal IDs.

Invoice status is one of pending, confirmed, expired or review. Checkout expires after expires_in_minutes (default 20, maximum 1440); the amount reservation lasts 90 days independently.

Invoice status lifecycle
Invoice created
pending
  • Exact amount receivedconfirmed
  • Checkout time ran outexpired
  • Amount reused or needs manual checkreview

Signed webhooks

Register a public HTTPS endpoint (port 443) in the dashboard. Its signing secret is shown once. Events: invoice.created, invoice.confirmed, invoice.expired, invoice.review and webhook.test. Deliveries retry up to eight times and can be retried manually.

Each request has X-SMeta-Event-Id, X-SMeta-Timestamp and X-SMeta-Signature headers. Compute HMAC-SHA256 over timestamp + "." + raw body, compare in constant time, reject timestamps older than five minutes, then parse JSON. Process each event ID once.

Verifying a webhook
  1. 1Read the raw body and X-SMeta-* headers
  2. 2Timestamp within 5 minutes?Reject (4xx)
  3. 3HMAC-SHA256(timestamp + "." + body)
  4. 4Constant-time compare with X-SMeta-SignatureReject (4xx)
  5. 5Event ID already processed?Return 2xx, skip
  6. 6Update the order and return 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;
}

Errors and rate limits

Errors return a JSON body with error.code and error.message. Common statuses: 400 validation, 401 authentication, 403 scope or origin, 404 not found, 409 conflict or review, 429 rate limited.

Invoice creation is limited to 120 requests per minute per merchant. Retry 429 and 5xx responses with backoff and the same Idempotency-Key.

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

Integrations

The dashboard includes 32 copy-ready recipes covering Node.js, NestJS, PHP, Laravel, Python, Django, Go, Java, C#, Ruby, React, Next.js, Vue and commerce platforms such as WooCommerce, Magento and Shopware. The downloadable WooCommerce plugin is a preview and does not yet accept live API keys.

Recipes are examples, not published SDKs. Frontend examples call your own backend, which holds the API key.

Payment confirmation

The gateway reads TRON mainnet through TronGrid for confirmed USDT TRC20 transfers to every receiving address with an open invoice, and rechecks each transfer against a successful solidified receipt. A transfer of the exact payable_amount between invoice creation and expiry confirms the invoice, and its transaction hash is stored as tx_hash.

A transfer that arrives after expiry, matches a previously used amount, or targets an invoice already in review sets the status to review and is never confirmed automatically. Detection usually takes a few minutes. Zero commission and a noncustodial design do not by themselves establish tax exemption or regulatory authorization.