295 lines
18 KiB
Markdown
Executable File
295 lines
18 KiB
Markdown
Executable File
<p align="center">
|
||
<img src="assets/logo/paygateway-wordmark.svg" alt="PayGateway" width="424" height="96" />
|
||
</p>
|
||
|
||
<p align="center">可扩展、可验证、面向授权业务集成的 Go 支付网关</p>
|
||
|
||
<p align="center">
|
||
<a href="https://github.com/lim39869-create/paygateway/actions/workflows/ci.yml"><img src="https://github.com/lim39869-create/paygateway/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
||
<img src="https://img.shields.io/badge/Go-1.25%2B-00ADD8?logo=go" alt="Go 1.25+" />
|
||
<img src="https://img.shields.io/badge/License-MIT-blue" alt="MIT License" />
|
||
<a href="https://goreportcard.com/report/github.com/lim39869-create/paygateway"><img src="https://goreportcard.com/badge/github.com/lim39869-create/paygateway" alt="Go Report Card" /></a>
|
||
</p>
|
||
|
||
# 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/<key>`),空 = 禁用 |
|
||
| `ALERT_WEBHOOK_URL` | 否 | 空 | 通用 Webhook URL(POST JSON:掉线 / 账本异常 / 风控文案检测) |
|
||
| `PUBLIC_RATE_MAX` | 否 | `60` | 公网接口滑动窗口内单 IP 最大请求数(仅覆盖 `/submit.php` `/api.php` `/qr/refresh` `/mq/:token`),0 = 禁用 |
|
||
| `PUBLIC_RATE_WINDOW_SEC` | 否 | `60` | 上一项的滑动窗口秒数,0 = 禁用 |
|
||
|
||
> ⚠️ **密钥三件套(`ACCOUNT_MASTER_KEY` / `LEDGER_PRIVATE_KEY` / `QR_TOKEN_SECRET`)更换/丢失的影响各不同,详见 `.env.example` 头部说明**。账户 / 上游密钥一律走管理后台,不在 `.env` 里。
|
||
|
||
### 3. 启动
|
||
|
||
**推荐:systemd 守护**
|
||
|
||
```bash
|
||
sudo cp deploy/paygateway.service /etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now paygateway
|
||
journalctl -u paygateway -f # 实时日志
|
||
sudo systemctl {status,restart,stop} paygateway
|
||
```
|
||
|
||
**裸跑:项目脚本(pid 文件 + 防双启)**
|
||
|
||
```bash
|
||
./start.sh # 后台启动,写 ./.gateway.pid,重复运行会拒绝
|
||
./status.sh # 查看运行状态
|
||
./stop.sh # 优雅停止(SIGTERM → 10s 后 SIGKILL)
|
||
./run.sh # 前台调试运行(go run,不读 .gateway.pid)
|
||
tail -f logs/paygateway.log
|
||
```
|
||
|
||
> 启动后看 `logs/paygateway.log` 的 `=== 启动配置自检 ===` 段:`0 错误` 才算就绪。
|
||
|
||
### 4. 配置账户(首次必做)
|
||
|
||
打开 `http://127.0.0.1:8081` 登录:
|
||
|
||
1. **上游对接** → 新增上游(pid + 密钥),否则下单会被拒绝
|
||
2. **账户池** → 按需新增:
|
||
- 商家通道:填 AppID + 私钥(`alipay` / `wechat`)
|
||
- 设备事件适配器:仅在获得授权的测试环境中配置
|
||
|
||
---
|
||
|
||
## 📡 下单 API(epay 协议兼容)
|
||
|
||
```bash
|
||
# 参数(MD5 签名,排除 sign/sign_type/pay_type)
|
||
curl "http://127.0.0.1:8080/submit.php?pid=1001&type=alipay&out_trade_no=TEST001&name=商品&money=10.00¬ify_url=https://your.com/notify&return_url=https://your.com/return&sign=MD5签名&sign_type=MD5"
|
||
```
|
||
|
||
- `type`:`alipay`(个人优先→商家回退)、`wxpay`、`personal_alipay`(强制个人)
|
||
- 返回:支付页(个人免签=套码二维码+备注码提示;商家=官方二维码/跳转)
|
||
- 异步通知:`trade_status=TRADE_SUCCESS` + MD5 签名
|
||
- 查单:`/api.php?pid=..&out_trade_no=..&sign=..`
|
||
|
||
---
|
||
|
||
## 🔒 安全设计
|
||
|
||
- 回调验签(商家 RSA2 / 个人 HMAC-SHA256)+ 时间戳防重放(±5min)
|
||
- 金额校验(回调金额 ≠ 订单金额 → 拒绝入账 + 异常池)
|
||
- 状态机条件更新(SQL `WHERE status=?` 互斥,防并发双写)
|
||
- 账本哈希链 + ECDSA 签名,篡改可检测
|
||
- 敏感密钥 AES-256-GCM 加密入库,仅内存明文
|
||
- JWT 管理后台鉴权(随机密钥,禁可预测)
|
||
- **后台任务 panic 自恢复**:全部后台 goroutine(关单扫描/收款码维护/账本日任务/隧道/告警等)带 `defer recover`,单点异常不拖垮进程,panic + 栈记录日志便于定位
|
||
|
||
---
|
||
|
||
## 🧪 测试
|
||
|
||
```bash
|
||
go vet ./...
|
||
go test ./...
|
||
go test -race ./internal/... # 全包并发安全验证
|
||
|
||
# 授权设备端到端测试(需要自行准备测试设备)
|
||
bash scripts/e2e_usb_realdevice.sh
|
||
```
|
||
|
||
覆盖:设备池隔离、动态调度、离线安全降级、通道窗口、均衡分配、设备健康埋点、事件匹配和账本哈希链。
|
||
|
||
---
|
||
|
||
## 📁 目录结构
|
||
|
||
```
|
||
cmd/gateway/ 主程序(路由/启动/配置/自检/动态关单与均衡分配装配)
|
||
internal/
|
||
account/ 账户池(调度/加密/风控/加权分配)
|
||
adbmgr/ adb 设备管理 / 配置下发
|
||
admin/ 管理后台 API + embed 前端
|
||
alert/ 告警客户端(Bark/Webhook)
|
||
channel/ 支付通道抽象 + 实现(alipay/wechat/personal)
|
||
epay/ epay 协议层(签名/参数)
|
||
ledger/ 审计账本(哈希链/快照)
|
||
ledgermon/ ADB 监听编排(设备健康埋点/公平调度/账本识别)
|
||
model/ 领域模型
|
||
notify/ 通知发送 worker
|
||
notifywatcher/ 通知栏事件驱动监听(比账本快 ~15s)
|
||
order/ 订单编排(状态机/幂等/结算/安全关单)
|
||
personal/ 个人免签(备注码/签名/风控)
|
||
pool/ WAIT_PAY 设备维度池(动态关单窗口/负载快照/CloseAt)
|
||
ratelimit/ 公网接口 IP 限流
|
||
reconciler/ 确认调度器/对账/异常池
|
||
scrcpy/ 真机投屏(adb forward)
|
||
store/ SQLite 存储层
|
||
tunnel/ Cloudflare 隧道
|
||
upstream/ 上游池(多商户)
|
||
web/ 支付页面生成
|
||
deploy/ systemd unit + 每日 SQLite 备份脚本
|
||
scripts/ 运维 / E2E 脚本
|
||
web/admin/ 管理后台前端源码(Vue3)
|
||
docs/ 设计文档
|
||
.env.example 配置示例(cp 为 .env 修改)
|
||
start.sh 裸跑启动(带 pid 文件 / 防双启)
|
||
stop.sh / status.sh 裸跑对应停止 / 状态查询
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 相关文档
|
||
|
||
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — 系统架构(协议/编排/通道三层)
|
||
- [`docs/PLAN-DYNAMIC-CLOSE-ALLOC.md`](docs/PLAN-DYNAMIC-CLOSE-ALLOC.md) — 动态关单窗口 + 设备/账户均衡分配设计与实现基线
|
||
- [`docs/MIANQIAN.md`](docs/MIANQIAN.md) — 设备自动化与个人通道的学习概览
|
||
- [`docs/RESILIENCE.md`](docs/RESILIENCE.md) — 弹性与对账(崩溃恢复/异常池/每日快照)
|
||
- [`docs/OPERATIONS.md`](docs/OPERATIONS.md) — 运维手册(升级/回滚/备份恢复/密钥轮换/常见故障/健康检查)
|
||
- [`docs/DESIGN-DEVICE-ACTOR.md`](docs/DESIGN-DEVICE-ACTOR.md) — ADB 设备操作串行化(DeviceActor 模型)
|
||
- [`docs/DESIGN-FAIR-SCHEDULER.md`](docs/DESIGN-FAIR-SCHEDULER.md) — 单设备多账户公平时间占比调度
|
||
|
||
## 📌 注意
|
||
|
||
- 本项目仅用于学习、授权测试和合法的商户支付集成。部署者必须自行确认支付合规、平台条款、隐私保护和数据安全要求。
|
||
- 设备自动化适配器不是第三方平台授权的替代品,不得用于未经授权访问账户、绕过安全控制或其他违法活动。
|
||
- 账本提供防篡改检测和可追溯性,不等同于法律、税务或司法意义上的审计结论。
|