可扩展、可验证、面向授权业务集成的 Go 支付网关
# PayGateway 支付网关 一个面向学习和授权业务集成的 Go 支付网关参考实现,兼容易支付(epay)协议,支持插件化商家通道、订单编排、可验证账本、异常对账和弹性调度。仓库同时保留设备自动化相关的通用工程实现,便于研究设备访问、事件处理和故障恢复。 > 详细设计见 [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)(架构)、[`docs/MIANQIAN.md`](docs/MIANQIAN.md)(设备自动化概览)、[`docs/RESILIENCE.md`](docs/RESILIENCE.md)(弹性/对账)和 [`docs/PLAN-DYNAMIC-CLOSE-ALLOC.md`](docs/PLAN-DYNAMIC-CLOSE-ALLOC.md)(动态调度)。 ## 界面预览 | | | |---|---| |  |  | | **登录页**(首次启动自动进入管理员注册) | **仪表盘**(订单/交易额/成功率/回调队列) | |  |  | | **支付通道**(多上游、按 pid 路由验签) | **订单管理**(状态、补账、动态关单) | |  |  | | **审计账本**(哈希链 + ECDSA 签名) | **系统设置**(运行时配置,密钥不入库) | ## 项目定位 `paygateway` 不是一个只展示接口调用的 Demo,而是一个经过真实业务验证的支付与账务基础设施参考实现。项目重点不在于堆叠支付渠道数量,而在于把以下问题放进同一套可审查的工程模型中: - 支付通道可插拔,核心订单流程不依赖单一渠道; - 订单状态、回调、补账和异常处理具备明确的状态边界; - 资金事件进入可验证的追加式账本,历史记录的静默修改可以被发现; - 管理后台、日志、备份、告警和测试构成完整的运行闭环; - 设备自动化适配器与订单、账本和异常池隔离,结果不明确时默认人工核查。 项目当前已验证支付宝、微信相关商家通道和设备事件适配能力。国际支付、数字资产支付及其他地区通道属于可扩展方向,尚不代表仓库已经提供对应的生产适配器。 ## 品牌资源 Logo 和 favicon 位于 [`assets/logo/`](assets/logo/),可用于项目主页、技术博客、后台界面和社区头像。标识的视觉语义是“插件化通道、统一编排和可验证账本链”。 ## 生产参考部署 `paygateway` 当前用于作者运营的 [FlashXAPI](https://flashxapi.com) 服务,承载真实的充值、订单、余额和账务流程。FlashXAPI 是作者自己的生产参考部署,不是独立第三方测评,也不是使用本项目的前置条件。 这意味着仓库中的订单、回调、账本和异常处理设计并非只在示例测试中存在,而是在实际运行环境中持续验证。生产部署中的用户数据、支付凭证、渠道信息和运行日志不会随仓库公开。 需要模型 API 的开发者可以单独了解 FlashXAPI;只希望研究或自部署支付网关的用户可以完全忽略该服务,直接使用本项目的开源代码。 ## 账务审计模型 项目的账本使用哈希链、ECDSA 签名、独立存储和数据库层约束,目标是让资金事件可追溯,并在历史记录被静默修改或删除时提供检测依据。 这里的“防篡改”是技术语义:正常业务流程下账本只追加,历史记录的异常变化可被检测。它不等同于绝对不可攻击,也不替代会计凭证、税务申报、司法鉴定或专业审计意见。签名密钥、运行主机、备份和访问权限仍需由部署者独立保护。 ## 为什么持续维护需要收入 本项目由个人维护,代码免费开源,不设置付费墙。持续维护、测试、生产运行和问题响应都需要时间与基础设施成本。作者通过自己运营的 FlashXAPI 服务获得维护收入,而不是向开源用户募捐或对源码设置获取门槛。 FlashXAPI 的生产使用关系会在项目发布、博客文章和技术实践记录中保持透明。服务可用性、模型能力和价格属于独立的服务承诺,不应被理解为本仓库对任何第三方渠道的保证。 ## 社区与实践记录 项目后续会通过配套博客发布架构设计、支付异常复盘、账务审计实践和版本更新记录;社区频道用于维护公告、问题交流和使用反馈。公开内容会优先讨论可复用的工程问题,不公开生产密钥、真实订单、用户请求、渠道来源或未经授权的账户操作细节。 - **Telegram 频道**:[@paygtway](https://t.me/paygtway) — 项目公告、版本更新和实践文章 - **Telegram 群组**:[加入群组](https://t.me/+TyBkhTb5HT0xNDFl) — 使用交流、问题反馈和社区讨论 如果你在实际部署中发现回调、账务、并发、备份或文档问题,欢迎通过 GitHub Issue 提供最小复现。请先阅读 [`SECURITY.md`](SECURITY.md),不要在公开 Issue 中提交密钥、支付凭证、个人数据或生产地址。 --- ## ✨ 功能特性 ### 商家通道(官方 API) - **支付宝**:PC/手机网站支付、当面付扫码(gopay v1.5.119) - **微信支付**:Native/JSAPI(预留扩展) - 多商户(上游池):每上游独立 `pid + 密钥`,下单/回调按 pid 路由验签 - 回调验签(RSA2 / HMAC-SHA256)、查单补账、超时关单 ### 设备自动化实验适配器(可选) - 设备访问、事件读取、状态机、调度和健康检查采用独立包封装 - 事件经来源认证、时间窗口、金额、状态和幂等校验后才可影响订单 - 设备离线、解析失败和状态冲突进入异常池,不自动确认资金结果 - 具体平台适配和授权边界见 [`docs/MIANQIAN.md`](docs/MIANQIAN.md);文档不提供第三方账户操作或绕过安全控制的步骤 ### 基础设施 - **账户池**:多账户轮询调度、日限额、连续失败熔断、手动/自动冻结,密钥 AES-256-GCM 加密入库 - **审计账本**:哈希链 + ECDSA 签名(`ledger.db`),每日快照,防篡改可验证 - **异常池**:金额不匹配 / 监听端离线 / 状态冲突 → 人工核查,绝不自动入账 - **不漏单保护**:个人通道关单前必查池内动态 `CloseAt` + 监听端离线检查——离线时订单进异常池人工核实(`LISTEN_OFFLINE`),绝不直接关单;商家通道关单前必查单(已支付→补账,查单失败→跳过) - **管理后台**:Vue3 + Element Plus 单页(embed 进二进制),仪表盘/通道/订单/账户/上游/对账/账本/日志 - **告警**:Bark / Webhook(掉线、账本异常、风控) --- ## 🚀 快速开始 ### 0. 两分钟最小体验(本地跑通,不接真实支付) ```bash git clone https://github.com/lim39869-create/paygateway.git && cd paygateway cp .env.example .env # 生成必备主密钥并覆盖示例值 sed -i "s/^ACCOUNT_MASTER_KEY=.*/ACCOUNT_MASTER_KEY=$(openssl rand -hex 32)/" .env go build -o gateway ./cmd/gateway/ && ./gateway # 打开 http://127.0.0.1:8081 → 首次访问自动进入管理员注册页 ``` 只启动了网关和后台,订单/账本/后台全部可用;真实收款需要另行配置支付通道密钥(见下文第 4 步起)。 ### 1. 编译 ```bash # Go 1.25+;隧道功能运行时需要系统 cloudflared go build -o gateway ./cmd/gateway/ # 可选:重新构建前端(需要 node 18+) cd web/admin && npm install && npm run build cd ../.. && go build -o gateway ./cmd/gateway/ ``` ### 2. 配置 `.env` ```bash cp .env.example .env && vim .env ``` `ACCOUNT_MASTER_KEY` 必须替换(`openssl rand -hex 32` 生成)。管理员账号不再配置在 .env——首次启动访问管理后台会自动进入注册页,凭据 bcrypt 哈希存主数据库 settings 表。 | 变量 | 必填 | 默认 | 说明 | |------|------|------|------| | `GATEWAY_ADDR` | 是 | `:8080` | 网关监听地址(易支付 /mq、套码 /qr 等) | | `ADMIN_ADDR` | 否 | `:8081` | 管理后台监听(Vue3 SPA + REST + WS) | | `JWT_SECRET` | 建议 | 空 | 留空 = 每次重启随机生成(重启后全员被踢下线);管理员凭据存主数据库 settings 表(bcrypt),首次启动自动引导注册 | | `ACCOUNT_MASTER_KEY` | 必填 | 空 | 账户 / 上游密钥加密主密钥。**缺失则服务拒绝启动**;首次用 `openssl rand -hex 32` 生成;**永不能改**(改 = accounts.db / upstreams.db 全部密文无法解密) | | `LEDGER_PRIVATE_KEY` | 建议 | 空 | 账本 ECDSA 私钥(PEM);留空 = 首次启动自动生成并持久化到 settings;更改则旧账本条目验签失败 | | `QR_TOKEN_SECRET` | 个人免签建议 | 空 | 套码 token 签名密钥;留空 = 首次启动自动生成并持久化到 settings;更改则旧套码失效 | | `ORDER_DB` | 否 | `data/paygateway.db` | 业务库路径(订单/异常池/回调队列/settings) | | `LOG_FILE` | 否 | `logs/paygateway.log` | 日志文件(slog JSON + lumberjack:50MB×10 份×30 天 压缩) | | `LOG_LEVEL` | 否 | `info` | `debug` / `info` / `warn` / `error` | | `ORDER_CLOSE_MINUTES` | 否 | `30` | **商家通道**未支付订单自动关闭分钟数(0=不自动关);商家走官方 API 可查单,固定窗口即可 | | `ORDER_CLOSE_MIN` | 否 | `5` | 个人免签**动态关单窗口下限**(分钟);0 或 ≥MAX → 退化为静态窗口 | | `ORDER_CLOSE_MAX` | 否 | `10` | 个人免签**动态关单窗口上限**(分钟);超过转人工异常池升级处理,不无限挂起 | | `ORDER_CLOSE_INTERVAL` | 否 | `30` | 关单扫描间隔秒数(动态窗口下建议 30s 起) | | `DEVICE_LOAD_CAP` | 否 | `0` | 单设备 WAIT_PAY 订单承载上限(0=按公式 `(MAX-60s)/poll成本` 自动算≈135);超限设备被均衡分配跳过 | | `DEVICE_OVERLOAD_RATIO` | 否 | `0.8` | 设备降权阈值(占 CAP 比例):负载进入缓冲带后分配降权,避免临界抖动 | | `DEVICE_POLL_COST_MS` | 否 | `4000` | 单次 poll 成本估算(ms),动态窗口与设备能力计算用 | | `EPAY_PUBLIC_BASE_URL` | 否 | 自动 | 网关对外地址(用于通知注册);同机 `http://127.0.0.1:8080`,公网填隧道 / 域名 | | `PERSONAL_BASE_URL` | 否 | =EPAY | 个人免签监听端可达地址 | | `ALIPAY_NOTIFY_URL` / `WECHAT_NOTIFY_URL` | 否 | `http://localhost:8080/...` | 通道回调地址,一般无需手填(隧道会覆盖) | | `PERSONAL_MAX_AMOUNT` | 否 | `50000` | 个人免签单笔限额(分;50000 = 500 元;0 = 不限) | | `PERSONAL_BIG_AMOUNT` | 否 | `20000` | 大额告警阈值(分;超此触发 Bark / Webhook) | | `PERSONAL_MAX_PER_MIN` | 否 | `30` | 单设备每分钟收款推送上限(防监听端异常刷消息) | | `ALERT_BARK_URL` | 否 | 空 | Bark 推送 URL(`https://api.day.app/