Skip to content

Repository files navigation

English · 简体中文

Xcash

开源 · 自部署 · 非托管加密货币支付网关

支持主流 EVM 链上任意 ERC-20 代币与 Tron USDT 收款, 资金经智能合约直达你自己的钱包:零平台手续费、免 KYC、全程不托管。

Website Docs GitHub Stars License Python Django

快速开始 · 官方文档 · API 参考 · 官网

电商账单 · USDT 充值 · 跨境结算 · SaaS 订阅 · 钱包 / 交易所

Xcash 管理后台 Dashboard

为什么选 Xcash?

托管式收款处理商站在你和你的钱之间:资金先进入他们的账户、按笔抽取手续费、要求 KYC 实名,还可能冻结你的账户,甚至直接关停服务。Xcash 反其道而行:网关跑在你自己的服务器上,钱从头到尾都是你的。

  • 非托管设计 —— 收款经极简智能合约流转,资金流向被写死为你的归集地址;系统不以私钥托管业务资金,即便被拖库也没有可供盗取的资产。Xcash 只负责账单匹配、确认与通知,不在资金路径上。
  • 零平台手续费 —— 自部署时不按交易抽成,只需承担链上 Gas;批量归集让单次归集的开销接近一笔普通转账。
  • 稳定币优先、多链覆盖 —— Ethereum、BNB Chain、Arbitrum、Base、Polygon、Optimism 等主流 EVM 链上任意 ERC-20;Tron 放行 USDT 与原生 TRX。
  • 一套系统、两种收款 —— 账单收款覆盖电商下单、订阅计费;充值收款提供交易所式专属地址,适合维护用户余额的平台。
  • 生产级配套开箱即用 —— 多商户多项目隔离、MistTrack 链上风控、可靠 Webhook、易支付 V1 兼容、Docker 一键部署。

与常见方案对比

Xcash(自部署) 托管处理商¹ BTCPay Server
资金托管 非托管,直达你的钱包 处理商代收,结算后放款 非托管
平台手续费 0 通常按笔抽 0.4%–1% 0
KYC / 开户审核 通常需要
EVM + Tron 稳定币 任意 ERC-20 + Tron USDT 视服务商而定 以 BTC 为主,其他币靠插件
交易所式充值地址 内置 少见
易支付 V1 协议 兼容

¹ 如 CoinPayments、NOWPayments、CoinGate 等。

快速开始

几分钟内启动一套生产网关。你需要一台装有 Docker 的 Linux 服务器和一个已解析到它的域名:

git clone https://github.com/xca-sh/xcash.git
cd xcash
./scripts/init_env.sh    # 生成 .env 并自动填充随机密钥
# 编辑 .env,设置 SITE_DOMAIN=pay.example.com
docker compose up -d

内置 Caddy 监听 127.0.0.1:6688,用你的反向代理(Nginx、Caddy 等)转发流量并配置 TLS。首次启动会创建后台账号 admin,其密码由 init_env.sh 随机生成——脚本执行完会打印一次,同时保存在 .envDJANGO_DEFAULT_SUPERUSER_PASSWORD请存入密码管理器。然后在管理后台完成三步:

  1. 为需要启用的公链填写 RPC 节点(QuickNode / Alchemy / Infura;Tron 需要 TronGrid API Key)。
  2. 为系统钱包在每条启用的链上充值少量 Gas。
  3. 创建项目、设置归集地址,通过 REST API 完成对接。

完整步骤见下方部署指南,更多内容见官方文档

不想自己运维?xca.sh 提供官方云服务——每月首 $500 交易额免手续费。

安全:为什么攻破服务器也偷不走钱?

假设部署 Xcash 的服务器被彻底攻破——数据库被拖库、密钥全部泄露。只要你的归集地址没有被篡改,你的资产就没有任何风险:事发前、事发中、恢复后用户完成的账单收款和充值收款,仍然会流入你的归集地址,因为服务器上不存在任何能改变资金流向的东西。

安全是 Xcash 与生俱来的结构性特性,而不是事后补上的功能:

  • Xcash 永不过手您的收款。 资金不会经过任何由系统代管的账户。
  • 合约收款的流向被写死。 收款智能合约只能把资金转给你的归集地址,攻击者改不动。
  • 收款合约极简。 逻辑唯一、攻击面为 0。
flowchart LR
    Buyer(["买家"])
    Contract["收款智能合约<br/>资金流向写死"]
    Wallet["你的归集地址<br/>私钥只在你手中"]

    Buyer -->|付款| Contract -->|只能流向| Wallet

    subgraph XcashBox["Xcash 系统 · 控制面"]
        Core["API / Worker / 数据库"]
    end
    Core -.->|匹配账单 · 状态 · 通知<br/>全程不经手资金| Contract

    Attacker["攻击者<br/>攻破服务器 / 拖库 / 密钥泄露"]
    Attacker -->|最多控制| XcashBox
    Attacker -- 改不动资金流向 --x Wallet

    classDef money fill:#e6f4ea,stroke:#34a853,color:#000;
    classDef danger fill:#fce8e6,stroke:#ea4335,color:#000;
    class Buyer,Contract,Wallet money;
    class Attacker danger;
Loading

资金路径(绿色)由智能合约写死,只在「买家 → 收款合约 → 你的归集地址」之间流动;Xcash 仅作控制面,负责账单匹配、状态流转与通知,不在资金路径上。因此攻击者即便完全控制 Xcash 系统,最多只能看到账单数据,无法改写合约里写死的资金流向。

账单收款 vs 充值收款

Xcash 提供两种入账方式,对接前请先区分:

  • 账单收款:账单式收款。每笔交易创建一张定额、限时的账单,买家付款后账单完成,适合电商下单、订阅计费等一次性收款场景。支持钱包直收与智能合约收款:合约模式下系统为每张账单分配独立收款地址,地址互不冲突、天然支持高并发,金额无需浮动。
  • 充值收款:交易所式充值收款。为每个用户分配专属充值收款地址,多链共享、实时监控,用户可随时转入、区块确认后入账,无需创建订单,适合需要维护用户余额的钱包、交易类业务。

账单收款自带买家端支付页,开箱即用、支持中英双语:

Xcash 账单收款支付页

特性

特性 说明
账单收款 定额、限时的账单式收款,适合电商下单、订阅计费等场景
充值收款 为每个用户分配专属充值收款地址,随时转入、确认后入账,体验同交易所
完全非托管 收款经智能合约直达你的钱包,Xcash 全程不过手资金
零平台手续费 不按交易抽成,只承担链上微量 Gas
多链多币种 覆盖主流 EVM 链,支持任意 ERC-20 代币;Tron 放行 USDT 与 TRX
多商户多项目 单实例隔离管理多个商户与项目,各自独立鉴权与归集地址
智能合约收款 EVM 链可为每笔账单生成独立合约收款地址,确认后自动归集
链上风控 接入 MistTrack 对账单收款、充值收款的来源地址做风险评分
Webhook 回调 实时推送账单收款、充值收款事件,自动重试,基于 Nonce 幂等去重
兼容易支付 支持标准易支付 V1 协议,便于平滑迁移
REST API 简洁的 RESTful 接口,HMAC-SHA256 签名认证
Docker 部署 Docker Compose 一键部署生产服务

接入你现有的技术栈

看你在跑什么系统,很多情况下根本不用写对接代码:

  • WooCommerce —— 官方 Xcash for WooCommerce 插件,几步为你的 WordPress 商店加上 USDT、USDC 等加密货币结账。下载插件
  • 易支付生态 —— 只要系统支持标准易支付 V1 协议,无需改造原有对接逻辑即可直接接入:Xboard、V2board、New API、独角数卡、异次元发卡(WHMCS 开发中)。

其余场景走 REST API:几个 HMAC 签名的调用即可创建账单收款、分配专属充值地址;Webhook 实时推送收款事件,并携带 MistTrack 风险评分。

链支持

功能 ETH BNB Chain Arbitrum Base Tron Polygon Optimism
账单收款
充值收款

更多 EVM 链只需在管理后台填写 RPC 节点即可启用。

代币支持

链类型 原生资产 代币标准 当前支持范围 启用方式
EVM ETH、BNB、POL 等原生资产(用于支付 Gas) ERC-20 支持任意 ERC-20 代币,按业务需要接入 USDT、USDC 或其他链上资产 在后台添加代币合约地址并启用对应公链
Tron TRX TRC-20 当前放行 USDT 与原生 TRX;其他 TRC-20 暂不作为收款资产 配置 Tron 链 RPC / TronGrid,并启用对应资产

Gas 成本与资金归集

启用智能合约收款后,系统会为每笔账单收款、每个充值收款用户分配独立的收款地址。很多人第一反应会担心:地址这么多,是不是每笔收款都要单独付一次 Gas 去归集?

答案是不需要。Xcash 内置两道归集闸门,把归集次数和 Gas 开销压到最低:

  • 归集延迟(周期性批量归集):收款确认后不会立即归集,而是等待一个可配置的延迟窗口再尝试。EVM 链默认 60 分钟,Tron 链默认 6 小时。同一地址在窗口内的多笔入账会被合并成一次归集,而不是每笔各付一次 Gas。
  • 归集金额门槛:到期尝试归集时,若该地址该币种的余额价值低于门槛(默认 1 USD),则跳过本次并继续累积,避免为几毛钱的"粉尘"付出更高的 Gas、注定亏本。

只有「延迟到达」和「金额达标」两个条件同时满足,系统才会自动发起归集交易。 未达标的地址不会被丢弃,后续入账会重新评估,攒够门槛后再归集。两道闸门的具体数值都可以在管理后台按链灵活调整。

Tron 资源策略(主网与 Nile 一致):VaultSlot 部署与归集只使用发送账户的储备能量。每次广播前重新模拟,只有可用能量覆盖预估值及安全余量时才发送,否则等待补充;fee_limit 按该能量预算和当前链上能量单价计算,不额外预留燃烧 TRX 的能量额度。此策略以预检至链上执行期间储备能量不被其他交易消耗为前提。带宽不足时允许燃烧 TRX 支付带宽费用,TRX 余额不足则等待补充。

同时,Xcash 的收款合约保持极简:每次归集的核心动作基本等同于对应代币的一次普通转账,所以单次归集的 Gas 消耗也基本接近该代币转账本身,几乎没有额外合约开销。

更巧妙的是,归集本身是一个无需许可、与安全无关的操作:

  • 归集合约的 collect() 没有任何权限校验,任何账户都能替你发起归集,调用方只是自付 Gas;而资金流向早已被合约写死为你的归集地址,谁来触发都改变不了结果。
  • 所以除了系统按闸门自动归集,你也可以在任何时间手动触发归集,不受延迟和门槛限制。
  • 无论自动还是手动、无论谁来调用,归集资金只会流入你的归集地址,Xcash 全程不过手(原理见「安全」)。

因此你只需为系统钱包保留少量原生资产用于支付归集 Gas(见部署步骤 6),无需为每笔支付或充币单独操心 Gas——真正的 Gas 开销,由你设定的归集频率与门槛共同决定。

内置风控接入

Xcash 内置的是风控查询、缓存、记录和展示能力;当前风险地址识别依赖外部 MistTrack(慢雾 MistTrack)服务,并非项目内部自行维护黑名单或自研链上风控模型。

风控系统当前覆盖两类核心资金入口:

  • 账单收款:账单匹配到链上付款后,系统会对付款方地址进行异步风险查询,并将风险等级和风险分数同步到账单收款记录。
  • 充值收款:充值收款记录创建后,系统会对转入资金的来源地址进行异步风险查询,并将风险等级和风险分数同步到充值收款记录。

风险结果会同时写入独立的风险评估记录,包含查询状态、目标类型、来源地址、交易哈希、风险等级、风险分数。管理后台可直接查看账单收款、充值收款和风险评估记录中的风险信息,便于运营人员进行人工复核、业务放行或进一步处置。账单收款和充值收款的 API/Webhook 输出也会携带 risk_levelrisk_score,方便商户系统同步展示或接入自己的处置流程。

Xcash 优先使用 MistTrack OpenAPI V3;未配置 MistTrack OpenAPI API Key 时,自动回退到 QuickNode MistTrack add-on。 两者都未配置,则不启用风控功能。

架构

graph LR
    Buyer["买家<br/>账单收款页"]
    Merchant["商家系统"]

    subgraph Xcash
        API["Xcash API"]
        Worker["Xcash Worker<br/>交易监听 · 归集 · 状态流转"]
        Wallet["Xcash 钱包引擎<br/>助记词托管 · 地址派生 · 交易签名"]
        Webhook["Xcash Webhook<br/>异步通知"]
    end

    Blockchain["区块链网络<br/>EVM · Tron"]

    Buyer -->|发起账单收款| API
    Merchant <-->|创建账单收款 / 查询| API
    API <--> Worker
    Worker <--> Wallet
    Worker <-->|监听 · 广播| Blockchain
    Webhook -->|推送事件| Merchant
Loading

部署指南

部署前准备

  • Linux 服务器,推荐 Ubuntu 22.04+ 或 Debian 12+
  • Docker 和 Docker Compose
  • 已解析到服务器 IP 的域名
  • 需要启用的公链 RPC 节点
  • 如需启用 Tron 账单收款,需要准备 TronGrid API Key

推荐服务器配置:

性能模式 硬件配置 可承载链数量
low 1 核 / 2 GB 2 - 3 条 EVM 链
medium 4 核 / 8 GB 8 - 15 条 EVM 链
high 8 核 / 16 GB 15 - 30 条 EVM 链

PERFORMANCE 为可设置到 .env 中的性能参数,可选值为 lowmediumhigh。不设置时默认使用 low

EVM 账单收款与充值收款都通过链上事件扫描感知和确认状态,且二者均默认启用、需要同时监听。实际可承载的链数量取决于 RPC 节点吞吐、区块出块速度和事件量,建议按上表保守配置性能档位。

1. 克隆项目

git clone https://github.com/xca-sh/xcash.git
cd xcash

2. 初始化环境变量

./scripts/init_env.sh

该命令会生成 .env,并自动填充运行所需的随机密钥和数据库口令。 如果 .env 已存在,脚本会拒绝覆盖并退出;如需重新生成,请先手动备份并删除旧文件。

3. 设置访问域名

编辑 .env 设置 SITE_DOMAIN

SITE_DOMAIN=xcash.example.com

请确保该域名的 DNS 已解析到服务器 IP,并配置 Nginx 或 Caddy 等反向代理,将流量转发至 http://localhost:6688

反向代理必须转发真实客户端 IP 与协议

这一步不是可选项。 网关的商户 IP 白名单、登录限流和全部 API 限流都以真实客户端 IP 为判定依据;HSTS 与 Secure Cookie 则依赖请求协议判定。如果你的反向代理没有转发这两项信息,Xcash 只能看到 Docker 网关地址,后果是:

  • 商户 IP 白名单对所有请求失效(配了白名单的商户会被全部拒绝);
  • 所有 IP 维度限流塌缩成「全站共用一个桶」,任何人都能打满登录限流,形成针对全体管理员的登录拒服;
  • Django 始终认为请求是明文 HTTP,HSTS 不下发。

Nginx 示例:

location / {
    proxy_pass http://127.0.0.1:6688;
    proxy_set_header Host $host;
    # 真实客户端 IP。必须用 $remote_addr(覆写语义)
    proxy_set_header X-Real-IP $remote_addr;
    # 原始请求协议,HSTS 与 Secure Cookie 依赖它
    proxy_set_header X-Forwarded-Proto $scheme;
}

⚠️ 不要用 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 作为唯一来源。那是追加语义,链最左侧的值来自客户端请求头、可被任意伪造。Xcash 优先采信 X-Real-IP,所以按上面写就是安全的。

Caddy 示例(Caddy 会自动转发 X-Forwarded-ForX-Forwarded-Proto,无需手动设置):

xcash.example.com {
    reverse_proxy 127.0.0.1:6688
}

若把 6688 端口对外暴露(在 .env 里设置 LISTEN_TO=0.0.0.0,例如反向代理部署在另一台机器),必须同时收紧受信代理范围,否则任何能连到该端口的来源都可以伪造客户端 IP:

# 默认为 private_ranges(信任私有网段),改成反向代理机器的具体 IP
CADDY_TRUSTED_PROXIES=203.0.113.10

可选:设置 ADMIN_PATH 将后台入口移动到自定义路径,例如:

ADMIN_PATH=secure-admin

未设置时后台仍挂在站点根路径,并会在后台右上角显示安全提醒。

4. 启动服务

docker compose up -d

启动脚本会先执行数据库迁移并补齐默认链、币种等主数据。首次启动时,如果数据库内还没有任何管理员账号,系统会自动创建后台账号 admin,密码取自 .env 中的 DJANGO_DEFAULT_SUPERUSER_PASSWORD(由 scripts/init_env.sh 随机生成,执行时打印过一次)。

出于安全考虑,生产环境(DEBUG=False)下该口令若为空、短于 12 位、或等于仓库内置的示例值,系统会拒绝创建管理员并中止升级——后台在未设置 ADMIN_PATH 时挂在站点根路径,弱口令等同于把超管拱手让人。

建议同时设置 ADMIN_PATH 把后台移到非默认路径。

5. 配置链 RPC

系统已预置主流链的基础信息,但 RPC 节点地址需要自行填写,网关才能与区块链通信。

登录管理后台,进入 区块链 → 公链 页面,为需要使用的链填写 RPC 地址。推荐使用 QuickNodeAlchemyInfura 等节点服务商。Tron 账单收款需要在 TronGrid 注册并获取 API Key。

6. 为系统钱包充值 Gas

登录管理后台,进入 系统 → 系统钱包 页面,复制系统钱包地址,并在每条启用的 EVM 链上向该地址充值少量原生资产用于支付 Gas,例如 ETH、BNB、POL 等。

系统钱包只用于平台基础设施交易,例如智能合约部署、智能合约归集等需要由系统主动发起的链上操作;业务收款资金仍按合约规则流向你的收款归集地址。这里不需要存入业务资金,只需要保留覆盖近期操作的小额 Gas,避免因 Gas 不足导致合约部署或归集任务无法广播。归集并非每笔收款各触发一次——系统通过归集延迟与金额门槛两道闸门批量归集,进一步压低 Gas 开销,详见「Gas 成本与资金归集」

7. 配置项目

登录管理后台,进入 项目 → 项目列表 页面,创建或编辑项目。项目是 API 对接的基本隔离单元,每个项目都有独立的 AppidHMAC密钥,用于接口鉴权与签名。

请至少确认以下配置:

  • IP 白名单:限制允许调用网关 API 的商户服务器 IP;测试阶段可使用 *,生产环境建议收窄到固定出口 IP 或网段。
  • 通知地址:用于接收账单收款、充值收款等 Webhook 事件;如暂未配置,项目会显示为未就绪。
  • 收款归集地址:业务资金最终流入的地址。启用智能合约收款或充值收款前必须配置 EVM 多签地址;该地址会写入智能合约规则,一旦设置不可修改。

API 对接

部署完成后,参考 API 对接文档 接入账单收款、充值收款和 Webhook 回调。仓库内的 API.md 提供完整接口参考。

创建账单收款时可传入账单收款级 notify_url 覆盖项目默认 Webhook;兼容易支付 V1 的 submit.php 入口也会将 notify_url 翻译为账单收款自身的通知地址。

备份与恢复

必须成对备份的两样东西

Xcash 的助记词以 AES-256-GCM 加密入库,加密密钥不在数据库里,而在 .envWALLET_MNEMONIC_ENCRYPTION_KEY。因此:

备份内容 位置 只有它的后果
Postgres 数据 db 容器 / db_data 助记词无法解密,等于没备份
.env 项目根目录 没有业务数据,等于没备份

两者必须作为一对、同一时点一起备份。 任何一份单独存在都无法恢复钱包。WALLET_MNEMONIC_ENCRYPTION_KEY 一旦丢失即不可恢复——热钱包私钥永久失效、归集能力全部失效。

scripts/upgrade.sh 在迁移演练中生成的 dump 不是备份:演练成功后会立即删除,只在演练失败时保留供排查。请另行建立独立的定期备份。

备份数据库

docker compose exec -T db sh -c 'pg_dump --format=custom --no-owner --no-privileges -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > xcash-$(date +%Y%m%d-%H%M%S).dump

建议:

  • 至少每天一次,并把 dump 与 .env 一起加密后传到服务器之外的存储(同机备份无法应对磁盘损坏与主机丢失);
  • .env 内容长期不变,可离线保存一份(如密码管理器或纸质),无需每天重传;
  • 定期做一次恢复演练——没验证过的备份不能算备份。

恢复

# 1. 停止业务服务,保留数据库
docker compose stop django worker worker-scan beat

# 2. 把 .env 恢复到位(务必是与该 dump 同时点的那一份)

# 3. 灌入 dump(目标库需为空库)
docker compose exec -T db sh -c 'pg_restore --exit-on-error --no-owner --no-privileges -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < xcash-YYYYmmdd-HHMMSS.dump

# 4. 拉起服务
docker compose up -d

运维命令

查看各服务运行状态:

docker compose ps

只有 dbredis 配了容器健康检查,因为它们被 depends_on: service_healthy 用作启动门控。 应用服务(django / worker / worker-scan / caddy)有意不配——Docker Compose 不会因 unhealthy 做任何事(自动重启是 Swarm 的能力),状态仅供 ps 展示,却要为此付出常驻开销。应用存活请用 下面的 HTTP 探针从容器外部监控。

停止服务(移除服务容器,保留数据库数据卷):

docker compose down

升级到最新版(确认当前位于 main 分支,手动拉取代码后执行生产升级):

git pull
./scripts/upgrade.sh

脚本只部署当前工作区,不执行 Git 拉取或分支切换;默认要求工作区干净。 脚本根据数据库实际待执行的迁移决定是否演练,在构建及必要的演练完成后, 自动停止旧 Beat,等待两组 worker 完成在途任务后,最后停止 Django;等待 worker 期间 HTTP 继续服务,异步任务暂存队列,待新版 worker 恢复后处理。生产迁移开始前,旧业务进程全部停止。 完成生产迁移及初始化后,先启动新版 Django、worker 和 Caddy,确认经 Caddy 的 /health 返回正常、两组 worker 已订阅各自主队列与周期队列,再启动 Beat。随后必须收到检查开始后 新发布、且由两组 worker 实际执行的心跳回执,脚本才报告升级成功。 没有待执行迁移时也执行这个切换顺序,使用者无需额外手动停止服务。 每阶段就绪等待默认最多 360 秒,可通过 APP_READY_TIMEOUT 设置。就绪失败返回非零状态; 启动 Beat 前的检查失败不会启动 Beat,启动后的调度检查失败会停止 Beat,保留应用进程供排查。 这些检查验证本机 HTTP、调度和消费链路;公网 TLS 与真实链 RPC 状态仍由外部监控覆盖。

生产迁移、基础数据与管理员初始化在同一个临时容器、同一个 Django 进程中按序执行。 即使没有待执行迁移,仍执行 migrate,保留 post_migrate 中的数据库触发器维护。 初始化错误使用专用退出码,确保脚本区分迁移失败与迁移完成后的初始化失败; 进程中断等无法确认迁移完成的情况,不自动恢复服务。 初始化成功后,脚本用临时 Compose override 以 /start --prepared 启动 Django,避免重复初始化; 普通 docker compose up -d 仍使用 /start 自动完成首次部署初始化,无需修改 .env。 初始化失败会重试一次,仍失败则保持业务服务停止;生产迁移失败不自动恢复服务。 就绪检查在 Django 容器内执行,不再额外创建临时容器。日志包含升级累计耗时、主要阶段耗时, 并在 HTTP 首次探测成功时报告自请求停止 Django 起的时间(包含关闭及探测开销,并非精确故障时长)。 就绪等待状态变化时立即输出,状态不变时每十秒输出一次。HTTP 恢复后可能仍需等待下一轮 30 秒周期的 Beat 心跳及任务执行回执,这段时间属于完整就绪检查,不等于 HTTP 停机。

正常停机使用 Celery warm shutdown:空闲 worker 立即退出,在途任务等待完成。 容器停止宽限为 330 秒,覆盖当前最长生产任务的 290 秒硬超时并预留清理时间; 这是等待上限,不是每次升级固定等待。新增更长的任务时,需要一起调整停止预算和监控窗口。

Django、两组 worker 和 Beat 共用本地 xcash-app:local 镜像(自定义 Compose 项目名时使用对应前缀), 仅 Django 声明应用镜像构建,Caddy 仍单独构建。完整 docker compose up -d 可在首次部署时构建所需镜像; 单独启动 worker 前需先执行 docker compose build django。升级脚本会自动构建应用和 Caddy。 Python 依赖与源码独立分层:只改源码时复用依赖安装和 .venv 复制层,减少镜像导出和解包开销。

本地构建会递归排除 .env*、备份和本地主网部署目录;运行密钥仅通过 env_file 注入。 自定义环境文件若采用其他文件名,应放在构建上下文之外,或显式加入 .dockerignore

两组 Celery worker 当前各运行一个容器,固定容器名为 xcash_workerxcash_worker_scan,通过 PERFORMANCE 档位调整并发。 将来需要 --scale 横向扩容时,先移除对应 worker 服务的 container_name;Beat 必须保持单实例。

Celery worker 分工

任务分两个消费组、由两个服务分别消费,互不抢占执行容量

服务 队列 职责
worker celery 及该组的周期专属队列 交易广播、确认、入账、Webhook 投递等业务任务
worker-scan scan 及该组的周期专属队列 各链充值扫描(受链 RPC 延迟支配,单任务硬超时 50s)

两者共用同一镜像与 PERFORMANCE 档位并发值——档位描述的是单个 worker 容器的并发。 扫描任务与业务任务同池时,链 RPC 持续劣化会把广播、 确认、Webhook 一起饿死,这是必须隔离的原因。

周期入口的队列合并由 common.redis_transport.Transport 完成:每个入口使用 periodic.<完整任务名> 专属队列,在同一段 Redis Lua 内检查全部优先级分桶,已有 消息就保留,队列为空才写入。正常发布与 unacked 重投共用此规则;worker 停机多久, 每种入口的待消费消息都最多一条。没有额外标记、TTL 或租约,也不依赖 worker 清理。 已经取走的消息属于 worker 的预取/执行容量,执行互斥继续使用 singleton_task

目前覆盖 config/periodic_tasks.py 登记的无参数入口:EVM/TRON 广播调度、 EVM/TRON 活跃链扫描调度、EVM 活跃链交易轮询调度,以及两组 worker 的消费心跳。带参数子任务、独立业务消息和其余 Beat 入口保留原行为。周期入口使用普通 @shared_task(ignore_result=True),必须在执行时 查询当前状态;参数、ETA/countdown、过期时间或 Canvas 在 transport 发布边界被拒绝。 apply_async() / delay() 保留 Celery 标准返回值,但返回的 AsyncResult ID 只标识本次 发布请求,不能据此认定它已独立入队或等待独立执行结果,因为该请求可能已被合并。 发布异常仍抛出,下一次 Beat tick 可以重新尝试。

现有 -Q celery-Q scan 启动参数无需修改:worker 在 celeryd_after_setup 时 自动订阅对应组的专属队列。手动 git pull 后统一通过 ./scripts/upgrade.sh 升级,脚本负责先停止旧 Beat 和 worker,再依次启动新版 worker、Beat,避免切换期间混用新旧投递逻辑。旧队列中的 积压仍由原消费组处理,本次改动不会清空业务队列。旧版缓存 hash xcash:celery:pending-once:v1 不再读取;全部进程升级后可删除该单独 key,无需清空 Redis。以后升级 Kombu 时需运行 xcash/common/tests/test_redis_transport.py,验证发布、优先级、前缀及恢复路径的兼容性。

存活监控(必须接入)

后台的“异常巡检”页、首页待关注事项与侧边栏提示已接入同一套消费心跳判定,打开页面时实时检查。 后台页面供人工查看;无人打开后台时的持续检测与主动通知,需要独立的外部监控服务。 容器内不做应用健康检查。请在 uptime 监控里配置以下三个 端点,非 200 或请求超时即告警。端点均无需鉴权,响应体只含 status 字段,失败细节仅写入结构化日志。

端点 200 503
GET /health 进程可服务请求,且 Postgres 可查询、Redis 可读写 至少一个硬依赖不可用
GET /health/scanning 所有活跃链的扫描都在正常周期内推进,且没有链处于失败状态 至少一条链调度停滞last_scanned_at 超过 SCAN_STALL_ALERT_AFTER_SECONDS,默认 300 秒未推进)或持续失败(扫描游标的 last_error_at 非空)
GET /health/workers 两组 worker 均有新鲜的调度与执行心跳 任一组心跳缺失、发布或执行超过 360 秒、或缓存不可用

/health 的 Redis 探测是写入后立即读回,因此能识别"连得上但写不进"——典型如 maxmemory 打满且无可淘汰键,此时 redis-cli ping 仍然正常。

/health/scanning 由 django 进程回答,与被监控的 Celery worker 属于不同故障域:worker 死亡、 beat 停摆、队列积压、broker 不可写都会命中「调度停滞」,RPC 凭据失效或节点持续报错则命中 「持续失败」。它有意不参与任何容器的 healthcheck——扫描停摆时 django 本身是健康的,混入会 导致误杀。

「持续失败」判据对单次 RPC 抖动敏感(一轮失败即成立,下一轮成功自动恢复)。请在监控侧配置 「连续 N 次失败才告警」——探针只如实反映当前状态,不做去抖。

/health/workers 补齐业务 worker 单独停摆的覆盖:Beat 每 30 秒向两组各发布一个无参数心跳, 复用周期队列合并机制,每种心跳最多留一条待消费消息。心跳在进程池内实际执行后写缓存, HTTP 只读缓存,不运行 inspect,也不因探测请求新增任务。发布时刻与执行时刻都须新鲜, 避免消费旧积压掩盖 Beat 停摆。首次启动在两组心跳执行前会返回 503;即使没有活跃链也会检查。 该探针验证调度和消费容量,不代替扫描事实、交易终态与 Webhook 送达结果的监控。

技术栈

  • 后端:Django 5.2 + Django REST Framework
  • 任务队列:Celery + Redis
  • 数据库:PostgreSQL
  • 区块链交互:web3.py(EVM)
  • 钱包派生:BIP44 HD 钱包(bip-utils)
  • 前端账单收款页:React 19 + Vite + Tailwind CSS
  • 部署:Docker Compose

路线图

  • Tron 链支持
  • Solana 链支持
  • 完善文档站

官方云服务

如果你不想自己部署和维护,可以直接使用官方托管版本:xca.sh —— 开箱即用、免部署、免运维、持续更新。

云服务按月交易额分段计费,每段只按本段费率收取:每月首 $500 免手续费,之后随交易量 1% → 0.8% → 0.6% → 0.4% 递减。自部署始终免费、零平台手续费。

支持

贡献

欢迎提交 Issue 和 Pull Request,参与方式见 CONTRIBUTING.md

如果 Xcash 帮你省了钱,欢迎点一个 ⭐——这能让更多商户发现这个项目。

License

MIT

About

开源、自部署、非托管的加密货币支付网关,支持多链账单收款与充值收款,资金经智能合约直达商户钱包。

Topics

Resources

Contributing

Stars

181 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages