文档

使用 SMetaGateway 进行开发。

从服务器创建 USDT TRC20 发票、引导买家进入收银台并通过签名 Webhook 确认付款所需的一切。

概述

SMetaGateway 是 TRON 上 USDT 的非托管发票层。商户登记公开收款地址,买家直接向该地址付款。网关为每张发票分配唯一的精确金额,跟踪其状态,记录会计数据并通知您的服务器。

所有 API 端点都位于本网站同源的 /api/v1 下。金额始终为最多六位小数的十进制字符串——绝不使用浮点数。

支付流程数据在您的服务器与网关之间传递;资金只会从买家钱包直接转入您的钱包。
您的服务器
SMetaGateway
买家
您的 TRON 钱包
  1. 您的服务器 → SMetaGateway1POST /api/v1/invoices,并附带 Idempotency-Key
  2. SMetaGateway → 您的服务器2返回包含 payable_amount 和 checkout_url 的发票
  3. 您的服务器 → 买家3将买家重定向到 checkout_url
  4. 买家 → SMetaGateway4买家打开付款页并扫描二维码
  5. 买家 → 您的 TRON 钱包5买家向您的地址发送精确的 USDT 金额
  6. SMetaGateway6网关在链上找到精确金额并确认发票
  7. SMetaGateway → 您的服务器7已签名的 invoice.confirmed Webhook
  8. 您的服务器 → SMetaGateway8GET /api/v1/invoices/{id} 核实后再履行订单

数据路径资金路径

快速开始

1. 创建工作区并添加 TRON 收款地址,复制其钱包 ID。

2. 打开“开发者 → API 密钥”,创建一个带有效期的密钥。密钥以 smg_ 开头,只显示一次,请妥善保存。

3. 使用下方请求从服务器创建发票。保存发票 ID 和精确的 payable_amount。

4. 将买家重定向到 checkout_url。只有在验证 invoice.confirmed Webhook 并在服务器端查询发票后,才能将订单标记为已付款。

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

身份验证

服务器集成使用 bearer API 密钥进行认证:Authorization: Bearer smg_…。密钥具有 invoices:read、invoices:write 和 ledger:read 权限,并可设置在 30、90 或 365 天后过期。过期密钥会返回 401 api_key_expired;可在控制台中轮换以获取新密钥。请将密钥保存在服务器上,切勿放入浏览器或移动应用。

官方控制台使用 HttpOnly 会话 Cookie。账户、钱包登记、API 密钥和 Webhook 管理均需要所有者会话,无法通过 API 密钥执行。

两种认证方式
您的后端
Authorization: Bearer smg_…
发票与账本导出
控制台所有者
由 /auth/login 设置的 HttpOnly smg_session Cookie
账户、钱包、API 密钥与 Webhook
Authenticated request
curl "https://smg.sabco24.com/api/v1/invoices/INVOICE_ID" \
  -H "Authorization: Bearer $SMETA_API_KEY"

金额与识别

以十进制字符串(如 "49.95")提交基础价格。网关会添加正向识别调整额——低于 100 USDT 时为 0.000001 至 0.009999 USDT,100 USDT 及以上为 0.000001 至 0.999999 USDT——并分别返回 base_amount、adjustment 和 payable_amount。

精确应付总额在所有商户之间按相同网络、代币和收款地址保留 90 天。优先使用未用过的总额;复用的总额会使发票进入 review 状态,而不会自动确认。

应付金额如何构成
基础价格49.95
识别调整0.004217
买家精确支付49.954217 USDT

按网络、代币和收款地址保留 90 天

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

发票与幂等性

POST /api/v1/invoices 需要 Idempotency-Key 请求头。使用相同密钥重复相同请求会返回原发票并带有 idempotent_replay: true;以不同内容复用密钥会返回 409 idempotency_conflict。

每个 merchant_order_id 只能对应一张发票。请将返回的发票 ID 和 payable_amount 与订单一起保存,并在 Webhook 到达时进行比对。

幂等结果
POST /invoices + Idempotency-Key
  • 新的密钥201 — 创建新发票
  • 相同密钥、相同请求体返回原发票,idempotent_replay: true
  • 相同密钥、不同请求体409 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_url 是一个授权链接:持有者即可查看对付款人安全的信息,请妥善保密。收银台显示商户名称、金额明细、收款地址、二维码和有效期,绝不显示您的元数据或内部 ID。

发票状态为 pending、confirmed、expired 或 review 之一。收银台在 expires_in_minutes(默认 20,最大 1440)后过期;金额保留期独立持续 90 天。

发票状态生命周期
发票已创建
pending
  • 收到精确金额confirmed
  • 付款时间已到expired
  • 金额被复用或需人工核查review

签名 Webhook

在控制台中登记公开的 HTTPS 端点(443 端口)。签名密钥只显示一次。事件包括 invoice.created、invoice.confirmed、invoice.expired、invoice.review 和 webhook.test。投递最多重试八次,也可手动重试。

每个请求都带有 X-SMeta-Event-Id、X-SMeta-Timestamp 和 X-SMeta-Signature 请求头。对 timestamp + "." + 原始请求体计算 HMAC-SHA256,进行常量时间比较,拒绝超过五分钟的时间戳,然后再解析 JSON。每个事件 ID 只处理一次。

验证 Webhook
  1. 1读取原始请求体和 X-SMeta-* 请求头
  2. 2时间戳是否在 5 分钟内?拒绝(4xx)
  3. 3HMAC-SHA256(timestamp + "." + body)
  4. 4与 X-SMeta-Signature 进行恒定时间比较拒绝(4xx)
  5. 5该事件 ID 是否已处理?返回 2xx,跳过
  6. 6更新订单并返回 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;
}

错误与速率限制

错误返回包含 error.code 和 error.message 的 JSON。常见状态码:400 校验失败、401 未认证、403 权限或来源不符、404 不存在、409 冲突或需审核、429 请求过多。

每个商户每分钟最多创建 120 张发票。对 429 和 5xx 响应请使用退避策略,并使用相同的 Idempotency-Key 重试。

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

集成

控制台提供 32 个可复制示例,涵盖 Node.js、NestJS、PHP、Laravel、Python、Django、Go、Java、C#、Ruby、React、Next.js、Vue 以及 WooCommerce、Magento、Shopware 等电商平台。可下载的 WooCommerce 插件为预览版,尚不支持正式环境 API 密钥。

这些示例并非正式发布的 SDK。前端示例调用您自己的后端,由后端保管 API 密钥。

付款确认

网关通过 TronGrid 读取 TRON 主网,查找转入每个有未结发票的收款地址的已确认 USDT TRC20 转账,并用成功的固化交易回执逐笔复核。在发票创建与过期之间转入精确 payable_amount 的转账会确认该发票,交易哈希保存为 tx_hash。

过期后到账、与曾使用过的金额相同,或对应已在审核中的发票的转账,会将状态设为 review,绝不会自动确认。检测通常需要几分钟。零佣金和非托管设计本身并不构成免税或监管授权。