b9f89f6526
新增 Gitea Actions 流水线,复刻本地 deploy.sh 到 CI,打 v* tag 即自动发版: - .gitea/workflows/deploy.yml:v*.*.* tag 触发 → go test → 交叉编译 linux/amd64 → 传 ali → 备份 payd.bak → 切换 → systemctl restart pay → 探活(失败自动回滚) - .gitea/workflows/checks.yml:PR/合并 main 跑 go vet+build+test 静态闸 - scripts/ci/deploy.sh:本地 deploy.sh 的 CI 版(从 secret 注入部署私钥 + 主机, 不用 ~/.ssh 的 ali 别名);notify.sh:Telegram 通知(secrets 未配则静默跳过) - runs-on mac,复用 jiu 的自建 runner;业务密钥仍走 ali /etc/pay/pay.env - README 部署段补 CI 路径 + 所需 secrets(ALI_SSH_KEY/ALI_HOST/ALI_USER) 首次需在 pay 仓 Gitea → Settings → Actions → Secrets 配 3 个 ALI_* secret。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UQmqWmV67sXGLrb3U1XXn
81 lines
4.5 KiB
Markdown
81 lines
4.5 KiB
Markdown
# pay — 独立支付服务
|
||
|
||
多业务 × 多渠道的收款服务。**支付宝(电脑网站支付)+ 微信(V3 Native 扫码)**(微信无沙箱,待真实商户号配置后可用)。
|
||
栈:Go 1.26 · Gin · GORM(SQLite 纯 Go 驱动,零 cgo)。
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
pay/
|
||
├── main.go # 启动:配置→DB→迁移→seed→路由→查单兜底
|
||
├── config/ # viper 配置(config.yaml + 环境变量覆盖)
|
||
├── internal/
|
||
│ ├── model/ # Merchant 商户凭证 / Product 套餐 / Order 订单 / NotifyLog 回调日志
|
||
│ ├── channel/ # Channel 渠道统一接口 + alipay 实现 + wechat 占位 + Registry 缓存
|
||
│ ├── service/ # OrderService:下单 / 回调验签入账 / 查单兜底
|
||
│ ├── handler/ # HTTP:收款页·套餐·下单·回调·查状态
|
||
│ ├── router/ # 路由装配
|
||
│ └── util/ # 响应 / 金额(分) / 订单号生成
|
||
├── web/ # result.html 支付结果页 · qrcode 二维码渲染
|
||
└── docs/ # 设计文档(index.html 索引)
|
||
```
|
||
|
||
## 核心约定
|
||
|
||
- **金额以服务端套餐价为准**,绝不信任前端传值。
|
||
- **回调必验签**(支付宝公钥)+ **核对金额** + **幂等防重**;成功才回 `success`。
|
||
- **到账以异步通知(notify)为准**,同步跳转仅展示;另有 `query_sync` 主动查单兜底。
|
||
- 多商户 / 多渠道:往 `merchants` 表加行即可,主流程不变。
|
||
|
||
## 接口
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|---|---|---|
|
||
| GET | `/result?out_trade_no=` | 结果页(return_url 落地) |
|
||
| GET | `/qrcode?text=` | 把码串渲染成二维码 PNG(微信 code_url / 支付宝扫码) |
|
||
| GET | `/health` | 健康检查 |
|
||
| GET | `/api/v1/products` | 上架套餐列表 |
|
||
| POST | `/api/v1/orders` | 网页支付下单,返回 `pay_url`(支付宝电脑网站支付跳转) |
|
||
| POST | `/api/v1/orders/qr` | 扫码下单,返回 `qr_code` 码串(微信 Native / 支付宝当面付) |
|
||
| GET | `/api/v1/orders/:out_trade_no` | 查订单状态(结果页轮询) |
|
||
| POST | `/api/v1/notify/alipay` | 支付宝异步回调 |
|
||
| POST | `/api/v1/notify/wechat` | 微信异步回调(V3 解密验签) |
|
||
|
||
## 沙箱联调步骤
|
||
|
||
1. 到 [支付宝沙箱](https://open.alipay.com/develop/sandbox/app) 拿 **APPID / 应用私钥 / 支付宝公钥**(公钥模式)。
|
||
2. 编辑 `config/config.yaml` 的 `alipay_sandbox`:`enabled: true`,填好 `app_id`,私钥/公钥建议走环境变量:
|
||
```bash
|
||
export ALIPAY_APP_PRIVATE_KEY="...应用私钥..."
|
||
export ALIPAY_PUBLIC_KEY="...支付宝公钥..."
|
||
```
|
||
3. 启动:
|
||
```bash
|
||
go run .
|
||
```
|
||
首次会自动建库、upsert 沙箱商户、补两个测试套餐(0.01 / 0.02 元)。
|
||
4. 浏览器开 `http://localhost:8080` → 选套餐 → 跳支付宝沙箱收银台 → 用**沙箱买家账号**扫码/登录付款。
|
||
|
||
> ⚠️ **异步回调需公网可达**:`notify_url` 由 `server.base_url` 拼成。本机只测「下单→跳收银台」用 `localhost` 即可;要验证 `notify` 回调到账,需把 `base_url` 换成内网穿透域名(frp/ngrok 映射到本机 8080),或部署到有公网的服务器。本机看不到回调时,`query_sync` 主动查单会兜底补记到账。
|
||
|
||
## 构建部署
|
||
|
||
**打 tag 自动发版到 ali(推荐)**:Gitea Actions 在 `v*` tag 上跑 `go test` → 交叉编译 → 传 ali → 备份 → 重启 → 探活(失败自动回滚)。
|
||
```bash
|
||
git tag v1.0.2 && git push origin v1.0.2 # 触发 .gitea/workflows/deploy.yml
|
||
```
|
||
- runs-on `mac`(复用 jiu 的自建 runner);PR/合并 main 另跑 `checks.yml`(vet+build+test)。
|
||
- **首次需在 pay 仓 Gitea → Settings → Actions → Secrets 配**:`ALI_SSH_KEY`(授权 ali root 的部署私钥整段)、`ALI_HOST`、`ALI_USER`(root);可选 `TELEGRAM_TOKEN`/`TELEGRAM_CHAT_ID`(发布通知,不配则跳过)。密钥只进 Gitea Secrets,不入库。
|
||
|
||
**本地一键发布(等价手动路径)**:
|
||
```bash
|
||
./deploy.sh # 交叉编译 linux/amd64 → 传 ali(ssh 别名)→ 备份 → 重启 pay → 探活(失败自动回滚)
|
||
```
|
||
|
||
手动构建:
|
||
```bash
|
||
go build -o payd . # 纯 Go,无需 gcc,可直接拷到阿里云 Linux 运行
|
||
```
|
||
|
||
生产改 `config.yaml`:`server.mode: release`、`base_url` 为正式 HTTPS 域名、商户 `production: true`;密钥走环境变量(`/etc/pay/pay.env`,Bitwarden 灌),不入库。
|