Files
pay/README.md
wangjia b9f89f6526 ci: 打 v* tag 自动发版到 ali(Gitea Actions)
新增 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
2026-07-05 12:16:36 +08:00

81 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# pay — 独立支付服务
多业务 × 多渠道的收款服务。**支付宝(电脑网站支付)+ 微信(V3 Native 扫码)**(微信无沙箱,待真实商户号配置后可用)。
栈:Go 1.26 · Gin · GORMSQLite 纯 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 → 传 alissh 别名)→ 备份 → 重启 pay → 探活(失败自动回滚)
```
手动构建:
```bash
go build -o payd . # 纯 Go,无需 gcc,可直接拷到阿里云 Linux 运行
```
生产改 `config.yaml``server.mode: release`、`base_url` 为正式 HTTPS 域名、商户 `production: true`;密钥走环境变量(`/etc/pay/pay.env`Bitwarden 灌),不入库。