123 lines
4.9 KiB
Markdown
Executable File
123 lines
4.9 KiB
Markdown
Executable File
# PayJS API
|
||
|
||
支付 API 服务,支持多支付渠道(XorPay、ZPAY),按项目模块化拆分。
|
||
|
||
## 路由与项目对应
|
||
|
||
| 项目 | 路由前缀 | 说明 |
|
||
|------|----------|------|
|
||
| nomadvip | `/nomadvip/*` | 固定 ZPAY,独立实例 |
|
||
| digital | `/digital/*` | 使用 PAYMENT_PROVIDER |
|
||
| cnomadcna | `/cnomadcna/*` | 使用 PAYMENT_PROVIDER |
|
||
| nomadlms | `/nomadlms/*` | 使用 PAYMENT_PROVIDER |
|
||
| legacy | `/payh5`、`/zpay_notify` 等 | 向后兼容 |
|
||
|
||
各项目业务互不影响,可单独配置。
|
||
|
||
## 环境
|
||
|
||
本地调试:`BASE_URL` 需为 ZPAY 可访问地址(如 ngrok 暴露 8700 端口)
|
||
线上部署:`BASE_URL=https://api.hackrobot.cn`
|
||
|
||
## 模块结构
|
||
|
||
```
|
||
app/
|
||
├── config.py # 配置(环境变量)
|
||
├── main.py # FastAPI 入口,挂载路由
|
||
├── sdk/ # SDK 组件化封装
|
||
│ ├── payment/ # 支付网关(ZPAY/XorPay)
|
||
│ ├── pocketbase.py # PocketBase 数据库
|
||
│ ├── minio.py # MinIO 对象存储
|
||
│ └── memos.py # Memos 用户(仅 nomadvip 使用)
|
||
├── services/ # 共享业务逻辑(基于 SDK)
|
||
│ ├── pb.py # PocketBase 客户端
|
||
│ ├── memos.py # Memos 用户创建
|
||
│ ├── minio.py # MinIO 上传
|
||
│ └── payment.py # 支付成功处理
|
||
├── routers/ # 项目路由
|
||
│ ├── nomadvip.py # /nomadvip/*
|
||
│ ├── digital.py # /digital/*
|
||
│ ├── cnomadcna.py # /cnomadcna/*
|
||
│ ├── nomadlms.py # /nomadlms/*
|
||
│ ├── legacy.py # /payh5 等(向后兼容)
|
||
│ └── common.py # /submit_meetup_application、/create_user
|
||
└── payment/
|
||
├── base.py # 支付抽象基类
|
||
├── xorpay.py # XorPay 实现
|
||
├── zpay.py # ZPAY 实现(易支付兼容)
|
||
└── factory.py # 渠道工厂
|
||
```
|
||
|
||
## 支付渠道
|
||
|
||
| 渠道 | 环境变量 | 说明 |
|
||
|------|----------|------|
|
||
| XorPay | `PAYMENT_PROVIDER=xorpay` | 默认 |
|
||
| ZPAY | `PAYMENT_PROVIDER=zpay` | 需配置 `ZPAY_PID`、`ZPAY_KEY` |
|
||
|
||
切换渠道:设置环境变量 `PAYMENT_PROVIDER=xorpay` 或 `zpay`,参考 `.env.example`。
|
||
|
||
## 本地调试
|
||
|
||
```bash
|
||
# 复制配置
|
||
cp .env.example .env
|
||
|
||
# 本地调试时 BASE_URL 需 ZPAY 可访问,可用 ngrok:
|
||
# ngrok http 8700 -> 将 BASE_URL 设为 ngrok 提供的 https 地址
|
||
|
||
python run.py
|
||
# 或 uvicorn app.main:app --host 0.0.0.0 --port 8700 --reload
|
||
```
|
||
|
||
## 线上部署
|
||
|
||
域名:`https://api.hackrobot.cn`
|
||
|
||
```bash
|
||
# .env 中设置
|
||
BASE_URL=https://api.hackrobot.cn
|
||
PAYMENT_PROVIDER=zpay
|
||
# 其他 ZPAY、PocketBase、Memos 配置见 .env.example
|
||
```
|
||
|
||
## 线上「支付失败」排查
|
||
|
||
本地正常、Ubuntu 部署后 api.hackrobot.cn 调用支付失败,常见原因:
|
||
|
||
1. **BASE_URL 配置错误**:必须为 ZPAY 可公网访问的地址。错误示例:`http://127.0.0.1:8700`、`http://localhost`。正确:`https://api.hackrobot.cn`(域名需解析到本机,nginx 反向代理对应端口)。
|
||
|
||
2. **服务器无法访问 zpayz.cn**:防火墙、DNS 或网络限制。访问 `https://api.hackrobot.cn/health?check_zpay=1` 自检:
|
||
- `zpayz_reachable: false` → 开放出站或检查网络
|
||
- `zpayz_error` 含 "SSL" → 执行 `apt install ca-certificates` 后重启
|
||
|
||
3. **client_ip 内网导致 ZPAY 风控拒单**:前端(digital/cnomadcna/nomadvip)通过 nginx 代理时,需配置 `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for`,否则 payjsapi 收到 127.0.0.1 会触发风控。已做兼容:内网 IP 时自动省略 clientip 参数。
|
||
|
||
4. **启动时控制台有 BASE_URL 警告**:说明配置了内网地址,需修改 `.env` 中的 `BASE_URL` 为公网地址后重启。
|
||
|
||
5. **查看详细错误**:支付失败时,payjsapi 会输出 `ZPAY create_order_api 失败` 日志,含 status/code/msg,便于定位。
|
||
|
||
## 自检结果(2026-04-01)
|
||
|
||
本次联动自检覆盖了 `nomadvip` 前端与本仓库 `nomadvip` 路由的支付闭环:
|
||
|
||
- 创建订单:`/nomadvip/payh5`、`/nomadvip/payh5/redirect`
|
||
- 异步通知:`/nomadvip/xorpay_notify`、`/nomadvip/zpay_notify`
|
||
- 支付查询:`/nomadvip/order_status`
|
||
- VIP 识别:`/nomadvip/check_vip`、`/nomadvip/check_vip_by_email`
|
||
|
||
### 发现问题
|
||
|
||
- 业务链路问题发生在前端 `nomadvip`:`/api/pay/confirm` 兜底写 `payments` 时使用默认金额,可能与实际支付金额不一致(例如测试价、活动价)。
|
||
|
||
### 处理状态
|
||
|
||
- 已在前端仓库修复:confirm 兜底写库时优先使用网关返回 `pay_price`,失败才回退默认金额。
|
||
- 本仓库暂未发现新的阻断型业务 bug(以静态代码审阅为准)。
|
||
|
||
## 维护建议
|
||
|
||
- 建议保持 `payjsapi` 为唯一支付入账源,前端 confirm 仅作为短时兜底。
|
||
- 建议为 `order_status` 返回结构固定增加字段约束(如统一返回 `pay_price` 字段),避免调用侧重复兼容。
|