# pay — 独立支付服务 多业务 × 多渠道的收款服务。**支付宝先行(PC 网页支付),微信留接口**(无沙箱,待真实商户号)。 栈: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/ # pay.html 收款页 · result.html 结果页 └── docs/ # 设计文档(index.html 索引) ``` ## 核心约定 - **金额以服务端套餐价为准**,绝不信任前端传值。 - **回调必验签**(支付宝公钥)+ **核对金额** + **幂等防重**;成功才回 `success`。 - **到账以异步通知(notify)为准**,同步跳转仅展示;另有 `query_sync` 主动查单兜底。 - 多商户 / 多渠道:往 `merchants` 表加行即可,主流程不变。 ## 接口 | 方法 | 路径 | 说明 | |---|---|---| | GET | `/` | 收款页 | | GET | `/result?out_trade_no=` | 结果页(return_url 落地) | | GET | `/health` | 健康检查 | | GET | `/api/v1/products` | 上架套餐列表 | | POST | `/api/v1/orders` | 下单,返回 `pay_url` | | GET | `/api/v1/orders/:out_trade_no` | 查订单状态(结果页轮询) | | POST | `/api/v1/notify/alipay` | 支付宝异步回调 | ## 沙箱联调步骤 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` 主动查单会兜底补记到账。 ## 构建部署 ```bash go build -o payd . # 纯 Go,无需 gcc,可直接拷到阿里云 Linux 运行 ``` 生产改 `config.yaml`:`server.mode: release`、`base_url` 为正式 HTTPS 域名、商户 `production: true` 并换正式密钥。