crypto 账户 region:"global" 与网关默认下单 region "cn" 不匹配被 picker 过滤 → no_account; crypto/USDT 本就 region 无关,region 留空即放行任意 region。
pay v2 — pangolin1 部署脚手架
pangolin1(103.119.13.48,Debian 12,root,内存 ~960Mi)上单独跑一份 payd
实例(pay-v2),只走 crypto(USDT-TRC20)+ 哪吒(nezha,CNY)双渠道,MOCK 关。
与阿里云那份 pay(仓库根 deploy.sh,支付宝+微信)彻底分开:不同 systemd
服务名(pay-v2)、不同端口(8090,旧 USDT watcher 已下线释放)、不同二进制/数据/
配置路径(/opt/pay-v2 /var/lib/pay-v2 /etc/pay-v2)、不同系统用户(payv2)。
文件清单
| 文件 | 用途 |
|---|---|
pay-v2.service |
systemd unit,装到 pangolin1 的 /etc/systemd/system/pay-v2.service |
pay.env.example |
env 模板,人工填真值后存到 pangolin1 的 /etc/pay-v2/pay.env(不进 git) |
config.yaml |
非密钥配置源,deploy.sh 首次部署会拷到 /etc/pay-v2/config.yaml |
deploy.sh |
部署脚本(交叉编译 + 上传 + 装 unit/config + 重启 + 探活回滚) |
部署步骤
-
先手工准备凭证(pangolin1 上,或本机
rbw get取值后scp/粘贴):ssh pangolin1 'mkdir -p /etc/pay-v2' # 把 pay.env.example 拷一份改名 pay.env,逐项填真值,再传上去: scp pay.env pangolin1:/etc/pay-v2/pay.env ssh pangolin1 'chmod 640 /etc/pay-v2/pay.env'需要人工填的 env(全部走 Bitwarden,建议条目命名见括号):
PANGOLIN_CRYPTO_1_ADDRESS—— TRC20 收款地址(Bitwarden:pay-v2 pangolin1 crypto-1)TRONGRID_API_KEY—— TronGrid API key(同上条目,或单独pay-v2 trongrid-key)PANGOLIN_NEZHA_1_PID—— 哪吒商户号(Bitwarden:pay-v2 pangolin1 nezha-1)PANGOLIN_NEZHA_1_PRIVATE_KEY—— 商户 RSA 私钥 PEM(同上条目)PANGOLIN_NEZHA_1_PLATFORM_PUBLIC_KEY—— 哪吒平台 RSA 公钥 PEM(同上条目)BIZ_PANGOLIN_SECRET—— 与 pangolin-server 的PAY_BIZ_SECRET同值 (Bitwarden:pay-v2 BIZ_PANGOLIN_SECRET,两边必须手工同步同一个值)
-
改
config.yaml里的占位域名(部署前,本地改好再让deploy.sh上传首个版本):biz.pangolin.callback_url的<pangolin-server-域名占位>换成真实域名。
-
跑部署脚本(pay 仓根目录,本机执行):
./deploy/pangolin1/deploy.sh首次会:建
payv2系统用户 → 建/opt/pay-v2/var/lib/pay-v2/etc/pay-v2→ 装 unit → 拷config.yaml(仅当远端还没有时)→ 传二进制 →enable --now→ 探活curl 127.0.0.1:8090/health。之后重跑只刷新二进制,config.yaml/pay.env都不会被覆盖(改配置要单独scp再systemctl restart pay-v2)。 -
验证:
ssh pangolin1 'systemctl status pay-v2 --no-pager' ssh pangolin1 'journalctl -u pay-v2 -n 50 --no-pager' ssh pangolin1 'curl -s 127.0.0.1:8090/health'
seed 生产套餐(占位命令,按需调整价格/名称)
pay 目前套餐通过代码里的 seed()/seedWechat() 之类函数在启动时 upsert,或者用
管理接口/直接写库。crypto 结算币种是 USDT,nezha 结算币种是 CNY——两个渠道各需
一套独立的 ProductPrice(同一 Product 下按币种分别挂价,不能共用一条同金额
不同币种硬凑)。生产 seed 前先确认落地方式(建代码里的 seed 函数,还是走管理接口),
以下只是占位:
# 占位:crypto(USDT)套餐价 —— 具体走哪个接口/表待定,先别直接照抄执行
# INSERT INTO product_prices (product_id, currency, amount, ...) VALUES (..., 'USDT', ...);
# 占位:nezha(CNY)套餐价
# INSERT INTO product_prices (product_id, currency, amount, ...) VALUES (..., 'CNY', ...);
渠道说明
- crypto:
method名为crypto。地址 watch-only,payd 不做归集——收到的 USDT 一直留在PANGOLIN_CRYPTO_1_ADDRESS这个地址上,不会自动转出。定期手动 归集/转出是运营动作,不在 payd 职责内。 - nezha:
method名预期为nezha(哪吒 provider 正在另一分支开发,尚未合并 进internal/providerbuild/registry.go的BuildRegistry)。本仓config.yaml已经把pangolin-nezha-1账户和credential_env_prefix结构定好,但在 provider 代码合并前,该渠道不会真正被注册(BuildRegistry只认识crypto/alipay/stripe/fake,未知 channel 的 enabled 账户会被静默忽略, 不会 fatal,只是收不到这个渠道)。provider 分支合并后需确认:- env 变量名是否确实是
PID/PRIVATE_KEY/PLATFORM_PUBLIC_KEY(本文档 按任务描述推导,以 provider 代码实际accts.Credential(accountID, "KEY")调用为准); - PEM 私钥/公钥在
pay.env里是单行原始换行还是\n转义,需和 provider 的 解析方式对齐(pay.env.example里留了两种可能的说明)。
- env 变量名是否确实是
已知易错点
QUERY_SYNC_ENABLED不是QUERYSYNC_ENABLED——viper 的"." → "_"替换对query_sync.enabled展开成QUERY_SYNC_ENABLED(带下划线),已用本地 viper 用例验证过。pay.env.example里已经用对的名字。- 绝对不要在
pay.env或config.yaml里设置MOCK_CHANNEL_ENABLED=true/mock_channel_enabled: true——会注册fake渠道并挂载/api/v2/dev/orders/:order_no/mark-paid这类联调专用端点,生产环境绝不能开。 - 阿里云那份
pay(仓库根deploy.sh)服务名是pay、端口8080、路径/opt/pay;这份是pay-v2/8090//opt/pay-v2,两者互不干扰,别把 两个deploy.sh搞混执行。