diff --git a/README.md b/README.md index fdfedce..31b6363 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,94 @@ # USDTMan -TRON USDT TRC20 收款监听服务 - 基于交易记录扫描 + 区块确认数验证 +TRON USDT TRC20 收款监听服务 -## 功能 +## 特性 -- 实时监听多个 TRON 地址的 USDT 收款 -- 区块确认数验证(默认6个确认) -- WebSocket 实时推送收款通知 -- HTTP API 管理监听地址 +- 扫描交易记录 + 区块确认数验证(默认 >= 6) +- 支持 `big.Int` 处理任意金额 +- WebSocket 实时推送 +- 主动查询历史交易(时间/金额/确认数过滤) +- 代理支持 -## API 使用方式 +## 使用 ```go -// 创建监听器(配置对象方式) uman := usdtman.NewUSDTMan(usdtman.Config{ - Addresses: []string{"地址1", "地址2"}, + Addresses: []string{"TN8nJ...", "TXYZo..."}, APIKey: "YOUR_API_KEY", - QueryInterval: 5 * time.Second, // 查询间隔(可选,默认 5 秒) - MinConfirmations: 6, // 最小确认数(可选,默认 6) - MaxHistoryTxns: 20, // 查询历史交易数(可选,默认 20) - ProxyURL: "http://127.0.0.1:7890", // HTTP/SOCKS5 代理(可选) + QueryInterval: 5 * time.Second, // 查询间隔 + MinConfirmations: 6, // 最小确认数 + MaxHistoryTxns: 20, // 监听查询数量 + ProxyURL: "http://127.0.0.1:7890", // 可选 }) -// 或者使用自定义 Transport -uman := usdtman.NewUSDTMan(usdtman.Config{ - Addresses: []string{"地址1"}, - APIKey: "YOUR_API_KEY", - Transport: &http.Transport{ - Proxy: http.ProxyURL(proxyURL), - // 其他自定义配置... - }, -}) - -// 设置收款回调 uman.OnPaymentComplete(func(payment *usdtman.USDTPayment) { - fmt.Printf("收到 %.6f USDT,确认数: %d\n", payment.Amount, payment.Confirmations) + fmt.Printf("收到 %s USDT (确认: %d)\n", + payment.GetAmountString(), payment.Confirmations) }) -// 启动监听 uman.Start() +defer uman.Stop() -// 停止监听 -uman.Stop() - -// 动态添加/移除地址 -uman.AddAddress("新地址") -uman.RemoveAddress("旧地址") +// 主动查询历史 +payments, _ := uman.QueryTransactions(usdtman.QueryFilter{ + Address: "TN8nJ...", + StartTime: startTimestamp, // 毫秒 + EndTime: endTimestamp, // 毫秒 + MinAmount: big.NewInt(10000000), // >= 10 USDT + MinConfirmations: 6, + Limit: 50, +}) ``` -## 运行 +## 运行 HTTP Server ```bash cd cmd/server -go run main.go +PROXY_URL=http://127.0.0.1:7890 go run main.go ``` 访问 http://localhost:8084 -## 接口 +## HTTP API - `POST /start` - 启动监听 -- `POST /stop` - 停止监听 -- `POST /add-address` - 添加监听地址 +- `POST /stop` - 停止监听 +- `POST /add-address` - 添加地址 - `POST /remove-address` - 移除地址 -- `GET /list-addresses` - 列出所有地址 -- `GET /payments` - 获取收款历史 -- `WS /ws` - WebSocket 连接 +- `GET /list-addresses` - 地址列表 +- `GET /payments` - 监听缓存记录(最多100条) +- `POST /query` - 主动查询历史 +- `WS /ws` - WebSocket 推送 -## 确认机制 +### 主动查询示例 -- 扫描地址的最近交易记录 -- 计算区块确认数(当前区块 - 交易区块) -- 仅在确认数 >= 6 时触发回调 -- 自动去重,避免重复处理 +```bash +curl -X POST http://localhost:8084/query \ + -H "Content-Type: application/json" \ + -d '{ + "address": "TN8nJ...", + "startTime": 1770000000000, + "endTime": 1770100000000, + "minAmount": "10000000", + "minConfirmations": 6, + "limit": 50 + }' +``` + +## 配置项 + +| 参数 | 类型 | 默认 | 说明 | +|------|------|------|------| +| `Addresses` | []string | [] | 监听地址 | +| `APIKey` | string | - | TronGrid API Key | +| `QueryInterval` | time.Duration | 5s | 查询间隔 | +| `MinConfirmations` | int64 | 6 | 最小确认数 | +| `MaxHistoryTxns` | int | 20 | 监听查询数量 | +| `ProxyURL` | string | - | HTTP/SOCKS5 代理 | +| `Transport` | http.RoundTripper | nil | 自定义 Transport | + +## 监听 vs 查询 + +- **监听** (`/payments`): 自动轮询最近交易,达到确认数后触发回调,缓存最多 100 条 +- **查询** (`/query`): 主动查询链上历史,支持条件过滤,不触发回调 diff --git a/cmd/server/main.go b/cmd/server/main.go index 9884517..bfb9fb6 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "log" + "math/big" "net/http" "os" "sync" @@ -79,6 +80,7 @@ func main() { http.HandleFunc("/remove-address", removeAddress) http.HandleFunc("/list-addresses", listAddresses) http.HandleFunc("/payments", getPayments) + http.HandleFunc("/query", queryTransactions) http.HandleFunc("/ws", handleWebSocket) http.HandleFunc("/", serveIndex) @@ -196,6 +198,59 @@ func getPayments(w http.ResponseWriter, r *http.Request) { }) } +func queryTransactions(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + jsonResponse(w, false, "Method not allowed", nil) + return + } + + var req struct { + Address string `json:"address"` + StartTime int64 `json:"startTime"` // 毫秒时间戳 + EndTime int64 `json:"endTime"` // 毫秒时间戳 + MinAmount string `json:"minAmount"` // 字符串格式的金额 + MinConfirmations int64 `json:"minConfirmations"` // 最小确认数 + Limit int `json:"limit"` // 查询数量 + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + jsonResponse(w, false, "invalid request", nil) + return + } + + if req.Address == "" { + jsonResponse(w, false, "address is required", nil) + return + } + + filter := usdtman.QueryFilter{ + Address: req.Address, + StartTime: req.StartTime, + EndTime: req.EndTime, + MinConfirmations: req.MinConfirmations, + Limit: req.Limit, + } + + // 解析最小金额 + if req.MinAmount != "" { + minAmount := new(big.Int) + if _, ok := minAmount.SetString(req.MinAmount, 10); ok { + filter.MinAmount = minAmount + } + } + + payments, err := uman.QueryTransactions(filter) + if err != nil { + jsonResponse(w, false, fmt.Sprintf("查询失败: %v", err), nil) + return + } + + jsonResponse(w, true, "success", map[string]interface{}{ + "payments": payments, + "count": len(payments), + }) +} + func handleWebSocket(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { diff --git a/index.html b/index.html index 2e7c358..5fa40af 100644 --- a/index.html +++ b/index.html @@ -34,11 +34,23 @@
| 时间 | 金额 | 来源 | 确认数 | TxID |
|---|---|---|---|---|
| ' + new Date(p.Timestamp).toLocaleString() + ' | ' + + '' + p.Amount.toFixed(6) + ' USDT | ' + + '' + p.From.substring(0, 10) + '... | ' + + '' + p.Confirmations + ' | ' + + '' + + p.TxID.substring(0, 10) + '... |