简介:一款针对原版彩虹易支付开发的USDT(TRC20)收款插件,面向需要为站点接入数字货币收款的站长及二次开发人员。插件基于TRC20网络,收款直达自有钱包,不经过任何第三方;配置项涵盖自动汇率或自定义兑换比例、订单超时时长等,能够灵活适配不同业务场景,同时支持PC与移动端支付场景。压缩包为rar格式,仅9KB,共含6个文件,核心为3个PHP脚本,分别负责支付接口对接、插件注册与回调监控,另附README说明文档、LICENSE许可证及404页面文件,便于理解结构、合规使用与快速排错。该资源已有245人学习,整体轻量紧凑;对于使用宝塔面板的服务器环境,包内的cron.php及相关说明提供了一套完整的回调监控思路,可辅助完成链上交易与订单状态的同步核对,降低漏单风险。适合已熟悉彩虹易支付基本操作、希望低成本快速获得USDT收款能力的用户,也可作为后续扩展功能的参考模板。
1. 彩虹易支付 USDT 插件解决的不只是“多一个币种”
个人支付站最常遇到的点,不是接不了支付宝和微信,而是面对海外用户、跨境小商品和数字商品时,整套结算体系突然断档。对方手里只有 USDT 钱包,你不可能让他去绑卡换汇,更不想为了这一点业务再单独写一套撮合订单、回调通知和账务对账的代码。彩虹易支付 USDT 插件解决的问题,就是让现有彩虹易支付系统在已经跑通的收银台上,多出一个 TRC20 网络的 USDT 支付通道,用户下单、扫码转账、插件检测链上到账、回调刷新订单,整条链路和原有支付宝通道共用同一套订单模型。本文站在部署和维护者的角度,把通道原理、参数取舍、测试方法和常见故障一次性讲透,适合维护彩虹易支付站点、准备在支付接口里添加加密稳定币收款的开发者看。
2. 彩虹易支付 USDT 插件的 TRC20 到账检测与回调接口原理
2.1 插件在彩虹易支付回调链路里的位置
彩虹易支付的订单流转是典型的“银联四要素”模型:用户在前端发起支付,彩虹系统创建一笔待支付订单,用户完成付款后,彩虹系统通过后台通知把结果回推给商户系统。接入 USDT 插件后,链上转账代替了银行扣款,但订单状态机不能变,否则商户侧全部要重写。
插件的核心工作有两步:第一步,替代“支付成功”这个事件来源;第二步,在确认链上转账真实有效后,代替原来的网关通知接口去驱动彩虹系统内部的状态更新。常见做法是,彩虹系统把 USDT 作为一个新增的支付方式注册进去,用户选择该方式时,拿到的是一个 TRC20 地址和应付金额,而不是支付宝二维码。用户完成转账后,插件侧轮询链上交易记录,校验到账金额和订单金额,校验通过后触发彩虹系统自己的回调机制,最后回复商户回调 URL。
这里的判断标准要记住:插件不是用户直接在链上付款时感知到什么,而是要把“链上状态”翻译成“彩虹订单状态”。翻译过程可以拆成四个环节:拉取交易记录、过滤本地址交易、校验金额和确认数、更新订单并触发回调。任何一个环节缺失,都会出现用户已经转账但商户迟迟收不到通知的情况。
2.2 USDT-TRC20 收款用轮询还是 Webhook
TRC20 链上转账确认有两个天然约束:一是转账事件产生后需要几秒钟到一个区块时间左右才会被公开的链 API 捕获;二是达到资金安全的确认数还需要一定时间。USDT-TRC20 常用确认数是 1 到 12,确认数越大,交易被回滚的风险越低,但用户的等待时间越长。
插件侧监听到账有两种方案。方案一是接入 TronGrid 的 WebSocket 或第三方交易推送服务,链上产生事件后立刻推送,延迟最低,但配置复杂,而且免费额度有限。方案二是定时轮询 TronGrid 或 TronScan API,每 15 到 30 秒拉取一次该收款地址最近的 TRC20 转账记录,这种方式的优点是实现简单、不需要维护长连接,也不会被事件服务商的配额卡脖子。我一般推荐轮询。USDT 的付款延迟增加十几秒对用户没有明显感知,但一个稳定投递、可以用本地脚本反复拉的对账接口,在生产环境里远比实时推送好排错。
轮询代码不需要做成本地常驻服务,放在彩虹系统原项目里,让一个定时任务每 20 秒触发一次即可。注意部分第三方免费 API 对单个 IP 的请求频率有限制,轮询间隔不能低于 10 秒,否则高峰期会撞限流。更稳的方式是在插件配置里区分“主网 API 地址”和“测试网 API 地址”,联调时使用测试网。
2.3 到账校验条件怎么组织才不会伪造订单
链上拉下来的交易记录不能直接用来改库。订单更新前至少要过四道校验,每一步不过则不能标记支付成功。这里给出核心判断逻辑的完整代码,按此顺序执行:
function checkTrc20Transfer(array $tx, array $order): bool { // 1. 地址白名单校验:只处理本插件的收款地址 if ($tx['to_address'] !== $config['usdt_address']) { return false; } // 2. 金额校验:使用字符串比较,避免 float 精度误差 if (bcsub($tx['amount'], $order['amount'], 6) !== '0.000000') { return false; } // 3. 确认数校验:低于阈值时等待 if ($tx['confirmed'] < $config['confirmations']) { return false; } // 4. 幂等校验:同一 txid 不能重复更新订单 if (orderAlreadyUpdated($tx['txid'])) { return false; } updateOrderPaid($order['order_id'], $tx['txid']); return true; }这段代码的四个校验缺一不可。地址校验防止把别的地址收款混进来;金额比较用bcsub而不是浮点等值判断,是因为 TRC20 合约转账金额在链上以 6 位小数返回,用float转换后可能出现 5.999999 和 6.000000 不相等的问题;确认数是为了防止交易因网络重组回滚;幂等校验保证同一笔链上转账不会在两次轮询中重复触发成功回调。
特别注意,第四步的幂等校验实现务必用数据库主键或唯一索引约束txid,而不是先查后改。两个轮询任务并发时,先查后改会同时通过检查,造成重复回调商户,最终商户侧收到两份通知。
2.4 确认数与交易状态的取舍
不同场景对确认数的要求不同。小额数字商品可以接受 1 个确认,因为 TRON 网络出块快,1 个确认在绝大多数情况下已经足够;大额订单建议至少等待 19 个确认,此时交易已经进入几十个区块,基本不可能被回滚。
| 确认数 | 到账速度 | 风险 | 适用场景 |
|---|---|---|---|
| 1 | 约 5 秒 | 极端情况下会被回滚 | 低价虚拟商品、自动发货 |
| 6 | 约 30 秒 | 低 | 常规电商订单 |
| 12 | 约 60 秒 | 极低 | 中高金额订单 |
| 19 | 约 100 秒 | 可视为最终 | 高价值单笔订单 |
插件里把确认数设为可配置项,而不是写死,方便运营人员根据客单价动态调整。这里的另一个细节是,确认数越大,用户等待期间发起售后投诉的可能性越高,在确认数为 1 到 6 之间已经能覆盖绝大多数场景。
提示:当用户实际转的金额和订单金额不一致时,不要自动按链上金额冲正订单。TRC20 转账金额一旦小于应付金额,必须转为人工处理,自动改单容易被人利用“零点几 U 差价”反复试探。
3. 彩虹易支付 USDT 插件的安装与核心参数配置
3.1 插件文件放进彩虹易支付的正确目录
不同版本彩虹易支付的可扩展目录略有差异。典型的彩虹系统会把支付接口放在inc/plugins/或系统自定的扩展目录中,也有的版本通过后台“支付接口管理”界面上传压缩包。安装前先确认你手里的插件包和目标系统版本,然后再解压上传。
上传后需要检查三个东西:插件主文件是否存在于插件扫描目录可发现的范围内;数据库是否自动执行了安装脚本;后台支付方式列表是否出现了付款方式 ID 为usdt的新选项。运行安装脚本可以使用命令行,也可以由插件配置页触发。为了避免少文件和目录权限问题,我习惯在上传后先执行一次全文件权限扫描,把 PHP 文件统一设为 644、目录设为 755,插件缓存目录单独设为 775。
建议把插件目录放到彩虹系统的扩展目录下,不要直接丢在根目录。原因是彩虹系统的路由规则可能对根目录新的入口文件有白名单限制,放在扩展目录下能自动继承该系统的路由能力,回调 URL 也不是额外入口地址,而更像是扩展模块内的一个内部动作。
3.2 参数配置表与每项取值建议
插件安装完成后,后台 ILS 会新增一块 USDT 配置,字段基本固定,主要参数如下:
| 参数名 | 默认值 | 说明 | 建议值 |
|---|---|---|---|
| USDT 收款地址 | 空 | 接收用户付款的固定地址 | 专用地址,不要与提现地址混用 |
| TRC20 主网 API | https://api.trongrid.io | 主网事件查询接口 | 保持默认,更换前先压测 |
| 测试网 API | https://nile.trongrid.io | Nile 测试网接口 | 仅在联调时启用 |
| 确认数 | 6 | 达到该值才更新订单 | 小额 1,常规 6,大额 12 |
| 轮询间隔 | 20 | 每次拉取交易记录的秒数 | 10 到 30 之间 |
| 轮询任务来源 | cron | 驱动轮询脚本的入口 | cron 每 1 分钟调用一次 |
| 商户回调超时 | 10 | 回复商户 URL 的等待时间 | 单次回调失败可自动重试 |
| 金额精度 | 6 | 金额保留小数位数 | 固定 6,不要修改 |
| 手续费比例 | 0 | 对用户收取的额外点数 | 0 到 2,需在订单页明示 |
API 地址建议在第一次配置时先用测试网跑通,再切主网。轮询间隔并不是越小越好,过小的间隔会在用户量上来后频繁触发 TronGrid 的限流。如果订单量大于每分钟 50 单,应该把固定地址的单次拉取改为按最近区块高度增量拉取,减少无效请求。
3.3 私钥和观察地址分开管理是关键
插件的天然职责是“查看”链上数据并更新订单,不需要替用户动钱包里的资产。绝大多数 USDT 插件被攻破的案例,问题不在合约层,而在私钥被明文存在了 web 目录里。
建议的做法是:线上只配置收款地址,私钥完全不出现在彩虹系统里;如果插件本身需要签名交易,把私钥放在 web 目录之外的独立文件,并使用环境变量注入。示例如下:
export USDT_PLUGIN_PRIVKEY_FILE="/data/secure/usdt.key" export USDT_PLUGIN_ADDRESS="TXYZ..."插件代码里,读取私钥时用getenv('USDT_PLUGIN_PRIVKEY_FILE')去指向文件路径,而不是把内容写在配置文件里。数据库表里可以存地址和公钥,私钥数据不落库,这样即使后台配置被越权读取,也不至于把钱包资产直接暴露。
补充一点:带自动提现功能的插件版本,通常会把提现私钥单独再存一把,与收款私钥分离。收款地址只收不转出,提现地址定期归集,这是最稳的钱包管理分层。
3.4 最小可用的支付接口接入示例
当商户侧想把 USDT 作为新的支付方式接入彩虹易支付时,原有的统一下单参数基本不需要大改,只需要把支付方式标识从alipay换成插件注册的usdt。
curl 'https://pay.example.com/submit.php' \ -d 'pid=10001' \ -d 'type=usdt' \ -d 'out_trade_no=20240601001' \ -d 'notify_url=https://merchant.example.com/notify' \ -d 'return_url=https://merchant.example.com/done' \ -d 'money=5.00' \ -d 'name=Digital Goods'type=usdt告诉彩虹系统走插件通道;out_trade_no是商户侧订单号,会在订单状态更新时原样回传,建议不要带特殊字符;money对 USDT 通道而言,以 USDT 单位计价,不要传人民币数量。
返回的数据里会包含qrcode字段,内容是 TRC20 地址字符串,前端可以直接生成二维码。商户侧需要做的只有三件事:把地址展示给用户、等待回调通知、进入回调处理逻辑。
4. 联调流程与 TXID 不落库的常见排查
4.1 用 TRON 测试网把整个链路跑通
正式收款之前,必须用测试网做一轮端到端验证。测试网使用nile网络,测试网 USDT 可以从 TRON 测试币水龙头申请。步骤如下:
第一步,把插件 API 地址切换到测试网地址,配置页确认数暂时设为 1,方便短时间看到结果。第二步,在测试网创建一个测试钱包作为付款方,向水龙头申请测试 USDT。第三步,在彩虹后台创建一笔 USDT 订单,拿到订单号和收款地址。第四步,用测试钱包向该地址转一笔与订单金额完全一致的 USDT。第五步,观察订单从“待支付”变为“已支付”,确认回调日志中收到商户通知。
整个流程正常后,再测三个边界场景:转错金额、不填备注转账、同一地址同时发起两笔不同订单的转账。测试通过后再切回主网配置。
测试网和主网之间唯一不变的是逻辑判断代码,差异只在 API 地址和币种合约地址。场景测试时注意,插件计算到账金额用的是智能合约的 transfer 日志,而不是钱包备注,备注有没有都不影响到账。
4.2 主网运行中的常见异常记录
以下是生产环境最常出现的几类问题,按出现频率排列:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 用户已转账,订单一直待支付 | API 地址配置成测试网 | 切回主网 API,重拉交易记录 |
| 回调日志出现金额不匹配 | 用户少转了部分,或金额精度丢失 | 打印链上原始金额,做余额比对 |
| 状态已更新但商户没收到回调 | 商户回调 URL 超时或返回非 success | 检查商户端日志,手动重推 |
| 确认数不达标卡住订单 | 确认数设过高 | 按场景重新调确认数 |
| 同一笔交易被两个订单抢到 | 两个订单金额相同 | 订单金额加入随机尾数 |
金额不匹配是排查中最容易踩的坑。USDT-TRC20 转账发出的金额可以包含多位小数,用户钱包里如果自己手动改了精度,实际到账和订单金额经常差零点几个 U。插件日志里不要只显示格式化后的金额,需要把链上原始字符串一并记录,这样方便判断是手误还是程序 bug。
4.3 用 curl 模拟回调判断是插件问题还是商户问题
当一笔订单链上状态正常但商户一直收不到通知,最直接的办法是把彩虹系统已经生成的回调参数原样重放一次。先从彩虹后台找到该订单的详细回调数据,然后用 curl 手动 POST 到商户回调地址:
curl -sS -X POST "https://merchant.example.com/notify" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "pid=10001" \ -d "trade_no=20240601135212345" \ -d "out_trade_no=20240601001" \ -d "type=usdt" \ -d "money=5.00" \ -d "trade_status=TRADE_SUCCESS" \ -d "sign=1a2b3c4d5e6f..."代码里trade_no是彩虹侧交易号,建议使用插件记录的链上txid,以便商户侧对账;sign使用商户密钥签出来的验签串,可以从原有日志中复制。若手动重放后商户正常入账,问题基本可以定位在回调任务的重试机制上;若手动重放依然失败,则要查商户签名校验逻辑。
4.4 轮询限流与拉取策略优化
使用 TronGrid 免费接口拉地址交易记录时,限流是最频繁访问的问题。每 20 秒一次的轮询在用户量大时会踩到按 IP 配额的限制。
常见优化方式是把一个轮询请求覆盖所有待支付订单,只调用一次“取地址最近 50 笔交易”接口,在代码中循环匹配不同订单,而不是每笔订单单独调一次接口。另一个措施是按上次拉取的区块高度做增量,只拿新高度之后的交易记录。彩虹插件的日志里如果出现429状态码,就说明已经被限流,需要调整轮询间隔或升级 API 套餐。
提示:主网轮询接口响应慢时候,先看当前 SDK 是否对
confirmed字段做了正确的解析。旧接口返回布尔值表示交易是否被打包,新接口返回的是区块高度,判断确认数要把当前最新高度减去交易所在高度。
5. 进阶小功能:收款地址轮换与定时链上对账
5.1 每笔订单一个收款地址的好处与实现
固定一个收款地址在业务上也能跑,但运营一段时间后会积累大量入账记录,对账困难且容易在区块浏览器上被人分析出业务规模。更稳妥的做法是给每笔订单分配一个新地址。
注意这里的“新地址”不是链上自动创建的零余额地址,而是插件预生成的一批地址池,订单创建时按顺序取一个未被使用的地址写入订单,并将地址和订单号建立映射。用户向该地址转款后,轮询任务按订单号关联的地址拉取交易,避免多个订单共用地址时无法区分资金。
$address = getAvailableAddressFromPool($order['order_id']); updateOrderAddress($order['order_id'], $address); markAddressBusy($address, $order['order_id']);getAvailableAddressFromPool从地址池中选一个未使用地址,markAddressBusy标记该地址已被订单占用。订单取消或过期后,过一段时间可以重新释放该地址。这个方案的代价是地址池需要定时补充冷地址,但换来的好处是每一笔链上入账都能直接映射到唯一订单,不再需要靠金额匹配。
5.2 五分钟级定时对账脚本
哪怕插件主流程完全正常,定时与链上资产做一次比对仍然值得做。对账脚本的核心逻辑是:拉取收款地址最近一段时间的 TRC20 入账,按地址映射找到订单,比对链上金额与订单应付款是否一致。若存在差值,向维护者发送告警。
*/5 * * * * cd /path/to/plugin && /usr/local/bin/php usdt-reconcile.php --date=today脚本执行时先收集本地已支付订单的 txid 集合,再去链上按地址拉交易,取差集补齐遗漏。这个脚本不能代替主轮询逻辑,它只负责在“主轮询异常但用户确实已付款”的场景下,把该收的钱收回来。上线前建议设置一个模拟错单,人为制造一笔已转账但未标记的订单,验证脚本能把它捞回。这样即使回调全部失败,资金也不会在链上滞留无人认领。
本文还有配套的精品资源,点击获取