@obeliskstudio/obelisk-usdt
v0.1.2
Published
ObeliskStudio 通用 USDT-TRC20 支付模块
Readme
ObeliskUSDT
需要定制或开发支付业务,联系 Telegram:
@Mhuai8(ObeliskStudio)
ObeliskUSDT 是一个专注 USDT-TRC20 的支付收款模块。
面向开发者提供可快速接入的 USDT 收款能力,支持网站与机器人双端场景。
模块定位很明确:只负责支付链路(下单、收款、链上确认、回调),不侵入宿主业务系统(商品、订单履约、会员发放等)。
私有化部署,资金直达你的钱包。
目标:让团队“少改代码、快速上线”完成 USDT 收款。
项目定位
- 核心目标:低改造、快接入、可扩展
- 适用对象:机器人项目、网站项目、SaaS、内容订阅、工具类付费功能
- 设计原则:支付模块独立,宿主业务解耦,避免后续维护互相牵连
为什么选择 ObeliskUSDT
- API 优先:通过 HTTP 路由快速接入,不绑定你的业务框架
- 自托管可控:部署在你自己的服务环境,数据与资金链路可审计
- 全流程闭环:下单、二维码、扫链、确认、回调一体化
- 双端复用:网页和机器人共用一套支付核心逻辑
- 对账友好:支持
bizOrderNo透传,方便和宿主业务单关联
特性
- 支持网页与机器人两种支付入口
- 创建订单、生成二维码、链上扫描、确认回调全流程
- 交易哈希去重,避免重复入账
- 订单归属校验,用户仅可查询/取消自己的订单
- 支持宿主业务订单号透传:
bizOrderNo - 核心数据表统一
obl_前缀,避免与宿主冲突
适用场景
- 你已有业务系统,只缺一个可落地的 USDT 收款能力
- 你希望机器人和网页共用同一套支付后端
- 你需要区分“业务订单号”和“支付订单号”,便于对账与审计
- 你希望后续可按项目扩展,不被单体支付逻辑绑死
边界职责(非常重要)
- 本模块负责:
- 生成支付订单与收款金额
- 分配收款钱包并返回支付二维码
- 扫链确认交易并更新支付状态
- 触发
onOrderConfirmed回调
- 宿主项目负责:
- 商品、定价、库存、会员时长等业务规则
- 权益发放(必须幂等)
- 用户系统与权限体系
- 财务对账策略与内部报表
依赖要求
- Node.js 18+
- Sequelize(推荐,模块运行依赖;宿主可继续使用 TypeORM)
- Redis
- TronGrid / TronScan API Key
与同类网关的定位差异
- 我们不是通用商城系统,而是“可嵌入的支付能力模块”
- 我们不接管你的商品与履约逻辑,只处理支付本身
- 我们强调“稳定可维护”与“可复制接入”
安装与获取
npm 安装(推荐)
npm i @obeliskstudio/obelisk-usdt在线使用(生产推荐)
你可以直接在宿主项目通过 npm 在线拉取并使用,不需要 clone 本仓库:
npm i @obeliskstudio/obelisk-usdt@latest安装后可用以下命令确认版本:
npm ls @obeliskstudio/obelisk-usdt包地址:
https://www.npmjs.com/package/@obeliskstudio/obelisk-usdt
5 分钟接入清单
- 准备数据库并执行迁移脚本(推荐使用内置迁移执行器)。
- 在宿主项目中初始化
initObeliskUSDT(...)。 - 挂载
paymentRouter与adminRouter。 - 配置并启动扫描器:
startScanner()。 - 实现
onOrderConfirmed幂等发放逻辑。 - 管理端先添加收款钱包,再开放支付入口。
数据库迁移(推荐)
包内提供 runObeliskUSDTMigrations(),用于自动执行 database/migrations/*.sql:
import { runObeliskUSDTMigrations } from '@obeliskstudio/obelisk-usdt';
await runObeliskUSDTMigrations({
sequelize,
logger,
});说明:
- 会自动创建迁移记录表:
obl_usdt_schema_migrations - 已执行迁移会自动跳过
- 不会在
initObeliskUSDT()时自动改库,需由宿主在部署/启动阶段主动调用
示例目录(当前状态)
仓库提供与当前接口契约一致的最小示例:
examples/backend/host-init.ts:宿主后端初始化与路由挂载examples/backend/payment-api-client.ts:服务端创建/查询/取消支付订单examples/web/create-and-poll.ts:网页端创建订单并轮询支付状态examples/bot/create-order-with-qr.ts:机器人创建订单并发送二维码examples/README.md:示例使用说明
示例按当前字段与路由编写,可直接作为接入参考。
快速接入
import { initObeliskUSDT } from '@obeliskstudio/obelisk-usdt';
const usdt = initObeliskUSDT({
sequelize,
redis,
logger,
config: {
network: 'mainnet',
webUrl: process.env.WEB_URL || '',
botUsername: process.env.BOT_USERNAME || '',
trongridApiKey: process.env.TRONGRID_API_KEY,
tronscanApiKey: process.env.TRONSCAN_API_KEY,
apiAuthToken: process.env.OBL_USDT_API_AUTH_TOKEN || '',
},
authMiddleware: {
optional: optionalAuth,
required: requireAuth,
admin: requireAdminAuth,
},
onOrderConfirmed: async (order) => {
// 宿主发放逻辑(必须幂等)
await benefitService.grantByOrderNo(order.orderNo);
},
});
app.use('/api', usdt.paymentRouter);
app.use('/api', usdt.adminRouter);
await usdt.startScanner();
usdt.registerScheduledTasks(cron);完整支付流程(宿主视角)
- 宿主先创建业务订单,得到
bizOrderNo。 - 调用
POST /payment/create创建支付订单,返回orderNo、金额、钱包地址、二维码。 - 用户转账后,扫描器监听链上交易并匹配订单。
- 状态按
pending -> paid -> confirmed -> completed推进。 - 到达确认阈值后触发
onOrderConfirmed。 - 宿主在回调里做幂等发放,并记录发放日志。
支付接口
用户接口:
POST /payment/createGET /payment/status/:orderNoPOST /payment/cancel/:orderNo
管理接口:
/admin/payment/wallets/*/admin/payment/orders/*/admin/payment/network*
创建订单参数
{
"bizOrderNo": "HOST-ORDER-10001",
"baseAmount": "99.00",
"ts": 1700000000,
"nonce": "e1b4f0f3b7d24e62a3d6c9f78b7a4b12",
"metadata": { "biz": "obeliskcard", "plan": "vip_year" },
"signature": "请看下方签名算法"
}字段说明:
bizOrderNo:必填,宿主业务订单号(用于对账与幂等发放)baseAmount:必填,支付基准金额(> 0,最多 2 位小数,建议用字符串传)ts:必填,签名时间戳(支持秒或毫秒)nonce:必填,签名随机串(建议 16-32 字节 hex)metadata:可选,宿主自定义字段(用于对账与回调发放)signature:必填,HMAC-SHA256 签名
说明:
POST /payment/create会返回orderToken。bizOrderNo幂等:同一bizOrderNo在“未过期 + 状态仍在支付中”时重复调用创建接口,会返回同一笔订单,不会重复创建与重复占用金额。- 查询/取消订单时,需要带上
orderToken:- Header:
x-obl-order-token: <orderToken> - 或 Query:
?token=<orderToken>
- Header:
签名算法(HMAC-SHA256)
POST /payment/create 默认要求签名,防止创建接口被刷单与参数被篡改。
同时启用 ts + nonce 防重放:
ts超出允许时间窗口会拒绝(默认 300 秒,可通过apiSignMaxSkewSeconds调整)nonce会写入 Redis(SET NX EX),按bizOrderNo + nonce作用域防重放- 签名比较使用
crypto.timingSafeEqual,降低时序侧信道风险
签名步骤:
- 从请求体中取这些字段参与签名(字段不存在或为空字符串则跳过):
bizOrderNo, baseAmount, ts, nonce, metadata
- 将参与签名字段按 key 的 ASCII 字典序排序,拼成
key=value&key=value的字符串。 - value 会做
encodeURIComponent,其中metadata会先做稳定 JSON 序列化。 - 使用
apiAuthToken作为密钥,对拼接后的字符串做 HMAC-SHA256,输出 hex 小写,作为signature。 签名示例(Node.js):
import crypto from 'crypto';
function stableStringify(value: any): string {
if (value === null || value === undefined) return '';
if (typeof value === 'string') return JSON.stringify(value);
if (typeof value === 'number') return JSON.stringify(Number.isFinite(value) ? value : null);
if (typeof value === 'boolean') return JSON.stringify(value);
if (Array.isArray(value)) return `[${value.map((v) => stableStringify(v)).join(',')}]`;
if (typeof value === 'object') {
const keys = Object.keys(value).sort();
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(',')}}`;
}
return JSON.stringify(String(value));
}
export function buildSignature(payload: any, apiAuthToken: string) {
const picked: Record<string, string> = {};
const fields = ['bizOrderNo', 'baseAmount', 'ts', 'nonce'];
for (const f of fields) {
const v = payload?.[f];
const s = v === undefined || v === null ? '' : String(v).trim();
if (s) picked[f] = s;
}
if (payload?.metadata && typeof payload.metadata === 'object') {
const raw = stableStringify(payload.metadata);
if (raw && raw !== '{}' && raw !== '[]') picked.metadata = raw;
}
const canonical = Object.keys(picked)
.sort()
.map((k) => `${k}=${encodeURIComponent(picked[k])}`)
.join('&');
return crypto.createHmac('sha256', apiAuthToken).update(canonical).digest('hex');
}网页接入建议
- 前端创建订单后展示二维码和倒计时
- 轮询
GET /payment/status/:orderNo刷新状态 - 超时后提供“重新下单”与“联系客服”入口
- 支付成功页展示
orderNo与bizOrderNo便于排查
机器人接入示例
const result = await usdt.bot.createOrderWithQR({
bizOrderNo: 'HOST-ORDER-10001',
baseAmount: '99.00',
metadata: { scene: 'bot' },
});
await bot.sendPhoto(chatId, result.qrPngBuffer, {
caption: `订单号: ${result.orderNo}\n金额: ${result.actualAmount} USDT\n地址: ${result.walletAddress}`,
});上线前检查清单
- 已添加至少 2 个可用收款钱包(避免单点故障)
onOrderConfirmed已做幂等保护(防重复发放)- 扫描器、任务调度已启动并可观测
.env已配置 API Key,代码中无硬编码密钥- 对外查询/取消订单时,
orderToken必须作为校验凭证
安全基线
- 查询/取消接口必须校验订单归属
onOrderConfirmed发放逻辑必须幂等- 密钥必须使用环境变量,禁止硬编码
- 扫描器对第三方链上接口启用短熔断降频,避免外部抖动拖垮主流程
性能与稳定性优化
- 钱包扫描启用固定并发池,提升多钱包场景吞吐。
- 扫描配置启用短缓存,减少高频配置读取开销。
- 扫描间隔自适应:有交易提速,无交易降频。
- 订单确认使用异步回调队列(重试 + 死信),避免慢回调阻塞主扫描链路。
- 管理统计接口
GET /admin/payment/stats返回扫描器健康指标(熔断与队列状态)。
数据表
obl_payment_walletsobl_payment_ordersobl_payment_transactions
订单状态流转
pending -> paid -> confirmed -> completed
使用说明
- 详细接入文档:
docs/使用说明-接入文档.md - 建议先看“用户使用流程(含加钱包)”章节再上线
版权与联系
- Copyright © 2026 ObeliskStudio. All rights reserved.
- 维护者:
@Mhuai8(ObeliskStudio) - 作者邮箱:
[email protected]/[email protected] - 工作室 Telegram:
@ObeliskStudio - 工作室业务:承接定制开发、支付系统、机器人、网站与各类技术外包合作
- 业务合作:需要定制或开发,联系
@Mhuai8
