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

4.5 KiB
Raw Permalink Blame History

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. 支付宝沙箱APPID / 应用私钥 / 支付宝公钥(公钥模式)。
  2. 编辑 config/config.yamlalipay_sandboxenabled: true,填好 app_id,私钥/公钥建议走环境变量:
    export ALIPAY_APP_PRIVATE_KEY="...应用私钥..."
    export ALIPAY_PUBLIC_KEY="...支付宝公钥..."
    
  3. 启动:
    go run .
    
    首次会自动建库、upsert 沙箱商户、补两个测试套餐(0.01 / 0.02 元)。
  4. 浏览器开 http://localhost:8080 → 选套餐 → 跳支付宝沙箱收银台 → 用沙箱买家账号扫码/登录付款。

⚠️ 异步回调需公网可达notify_urlserver.base_url 拼成。本机只测「下单→跳收银台」用 localhost 即可;要验证 notify 回调到账,需把 base_url 换成内网穿透域名(frp/ngrok 映射到本机 8080),或部署到有公网的服务器。本机看不到回调时,query_sync 主动查单会兜底补记到账。

构建部署

打 tag 自动发版到 ali(推荐)Gitea Actions 在 v* tag 上跑 go test → 交叉编译 → 传 ali → 备份 → 重启 → 探活(失败自动回滚)。

git tag v1.0.2 && git push origin v1.0.2   # 触发 .gitea/workflows/deploy.yml
  • runs-on mac(复用 jiu 的自建 runner);PR/合并 main 另跑 checks.ymlvet+build+test)。
  • 首次需在 pay 仓 Gitea → Settings → Actions → Secrets 配ALI_SSH_KEY(授权 ali root 的部署私钥整段)、ALI_HOSTALI_USERroot);可选 TELEGRAM_TOKEN/TELEGRAM_CHAT_ID(发布通知,不配则跳过)。密钥只进 Gitea Secrets,不入库。

本地一键发布(等价手动路径)

./deploy.sh      # 交叉编译 linux/amd64 → 传 alissh 别名)→ 备份 → 重启 pay → 探活(失败自动回滚)

手动构建:

go build -o payd .      # 纯 Go,无需 gcc,可直接拷到阿里云 Linux 运行

生产改 config.yamlserver.mode: releasebase_url 为正式 HTTPS 域名、商户 production: true;密钥走环境变量(/etc/pay/pay.envBitwarden 灌),不入库。