# PaymentCenter 支付中心 一个基于 Go + Gin 的统一支付网关系统,为商户提供下单、退款、查询、收银台等支付能力,底层对接微信支付、支付宝等第三方支付平台。 > 代码位置:后端接口位于 `app/`,收银台页面模板位于 `front/templates/`。 --- ## 1. 技术栈 | 组件 | 选型 | |------|------| | 语言 | Go | | Web 框架 | [Gin](https://github.com/gin-gonic/gin) | | ORM | [xorm](https://xorm.io/) | | 第三方支付 | [go-pay](https://github.com/go-pay/gopay) | | JSON | [bytedance/sonic](https://github.com/bytedance/sonic) | | 参数校验 | go-playground/validator.v9 | | 缓存/分布式锁 | Redis(snow-core/redis) | | 数据库 | MySQL | | 加密算法 | RSA / SM2 / SM4 / AES / DES3 | --- ## 2. 目录结构 ``` PaymentCenter/ ├── app/ │ ├── constants/ # 常量定义(错误码、公共枚举、pojo) │ │ ├── common/common.go # 订单状态、支付渠道、路由前缀 │ │ └── errorcode/ # 错误码与中文消息 │ ├── http/ │ │ ├── routes/route.go # 路由注册入口 │ │ ├── requestmapping/ # 路由→请求结构体映射 │ │ ├── middlewares/ # 中间件(CORS、鉴权、加解密、参数校验) │ │ ├── controllers/ │ │ │ ├── base.go # 公共:Success/Error/GenRequest/ApiRes │ │ │ ├── front/ # C 端:支付 API、收银台、回调 │ │ │ └── backend/ # 管理后台 │ │ └── entities/ # 请求/响应结构体 │ ├── services/ │ │ ├── api_request_valid.go # App 校验 & IP 白名单 │ │ ├── order.go # 订单 CRUD & 状态检查 │ │ ├── pay_page.go # 收银台渠道列表、订单状态检查 │ │ ├── apicrypt/ # RSA/SM2/SM4 加解密实现 │ │ └── thirdpay/ # 第三方支付业务封装 │ │ ├── pay.go # 下单/退款/关闭主流程 │ │ ├── pay_page.go # 收银台业务:V2 预下单、查询 │ │ ├── pay_query.go # 三方查询 │ │ ├── do/ # 渠道策略层(PayCheck、Pay、PayWay 映射) │ │ └── thirdpay_notify/ # 回调异步通知处理 │ ├── third/paymentService/ # go-pay 封装(微信/支付宝底层调用) │ ├── models/ # xorm 实体(orders、app、merchant、pay_channel…) │ ├── data/ # Repository 层(OrderRepo、PayChannelRepo 等) │ ├── utils/ # snowflake、JWT、HTTP client、useragent、DES │ │ └── encrypt/ # AES、RSA、SM2、SM4 │ ├── caches/ # 缓存 │ └── mq/ # 消息队列 └── front/ └── templates/ ├── payPage.html # 收银台主页面 ├── payTemplateDefault.html # 渠道跳转模板 ├── success.html # 支付成功(JSAPI 场景) └── fail.html # 支付失败 ``` --- ## 3. 架构分层与调用链 ``` Gin 路由 │ ▼ Middleware(CORS → ValidateRequest / ValidatePayRequest → 加解密) │ ▼ Controller(front/ 或 backend/) │ ▼ services/(业务编排 + services/thirdpay/) │ ▼ services/thirdpay/do/(PayCheck → Pay → PayWay 策略) │ ▼ third/paymentService/(go-pay 底层调用)→ 微信 / 支付宝 │ ▼ data/(Repository)→ models/(xorm ORM)→ MySQL ``` - **控制器层**:只做 HTTP 层的事(绑定请求、调用 service、封装返回) - **服务层**:核心业务(状态机、幂等、分布式锁、渠道路由) - **Repository(data/)**:只做数据库 CRUD,不包含业务逻辑 --- ## 4. C 端接口清单 > 接口前缀:`/pay/front/api/v1` 或 `/pay/front/api/v2` | 方法 | 路由 | 中间件 | 功能 | |------|------|--------|------| | POST | `/notify/wx/:payChannelId` | - | 微信支付回调 | | POST | `/notify/ali/:payChannelId` | - | 支付宝支付回调 | | POST | `/pay/url` (V1) | ValidatePayRequest | 下单,同步返回支付 URL / JsInfo | | POST | `/pay/url` (V2) | ValidatePayRequest | 预下单,仅创建订单,返回收银台 URL | | POST | `/pay/refund` | ValidatePayRequest | 退款 | | POST | `/pay/query` | ValidatePayRequest | 查询订单状态 | | POST | `/pay/close` | ValidatePayRequest | 关闭订单 | | GET | `/payPage?no={id}` | - | 收银台 HTML 页面入口 | | POST | `/payPage/list` | ValidateRequest | 获取可用支付渠道列表 | | GET | `/payPage/submit` | ValidateRequest | 获取支付跳转链接(渲染 payTemplateDefault.html)| | POST | `/payPage/query` | - | 收银台页面轮询订单状态 | | POST | `/ali/getOauth` | ValidateRequest | 支付宝小程序获取 openId | | POST/GET | `/wx/*` | ValidateRequest | 微信授权 / openId / JSAPI / 小程序支付 | - 路由定义参见 [app/http/routes/route.go](file:///e:/goproject/goproject/PaymentCenter/app/http/routes/route.go) - 路由→结构体映射参见 [app/http/requestmapping/front.go](file:///e:/goproject/goproject/PaymentCenter/app/http/requestmapping/front.go) --- ## 5. 两种下单模式 ### 5.1 模式一:V1 直接下单(`POST /pay/front/api/v1/pay/url`) 适用于商户自己管理收银台页面: 1. 商户构造请求 `RequestBody{ app_id, timestamp, data, key }`,加密后发起 2. 中间件 `ValidatePayRequest()`:查 App 配置 → 解密 → 反序列化为 `PayReqs` 3. 控制器调用 `thirdpay.ThirdPayInfoCheck()` → `thirdpay.ThirdPayUrl()` 4. 同步返回 `ApiResponse{ Order, Url, JsInfo }` ### 5.2 模式二:V2 预下单 + 收银台(推荐) 适用于商户跳转至我方收银台页面: 1. 商户调用 `POST /pay/front/api/v2/pay/url` 2. `PayUrlV2Service()`: - Redis 分布式锁(`payUrl:{out_trade_no}`,3 秒) - 检查商户是否有可用支付渠道 - 按 `out_trade_no + app_id` 查询订单;不存在则新建订单(状态 WAITPAY) - 返回收银台 URL:`https://pay-host/pay/front/api/v1/payPage?no={orders.id}` 3. 商户将用户浏览器跳转到收银台 4. **收银台页面**(`payPage.html`): - 读取 URL 参数 `no`(**订单自增 ID,不是 out_trade_no**) - `POST /payPage/list?id=xxx` 拉取渠道列表 - 单渠道直接跳转 `/payPage/submit`,多渠道让用户选择 - 选择后跳转到第三方支付页面 - 支付完成后轮询 `/payPage/query`,成功后跳转到商户 `return_url` --- ## 6. 收银台页面流程 ``` 商户后台 │ POST /v2/pay/url ─────┐ │ ▼ │ 支付中心创建订单 │ │ │ ▼ │ 返回 payPage?no={orderId} │ └── 用户浏览器 ───► GET payPage.html │ ├── JS 调 POST /payPage/list 获取渠道 │ ├── GET /payPage/submit 获取跳转链接 │ └──► payTemplateDefault.html window.location │ ├── POST /payPage/query 轮询订单状态 │ └── 支付成功 → 跳转 return_url ``` 页面文件: - [payPage.html](file:///e:/goproject/goproject/PaymentCenter/front/templates/payPage.html) — 收银台主页(含加载态、渠道选择、状态确认弹窗) - [payTemplateDefault.html](file:///e:/goproject/goproject/PaymentCenter/front/templates/payTemplateDefault.html) — 第三方支付跳转 - [success.html](file:///e:/goproject/goproject/PaymentCenter/front/templates/success.html) — 微信 JSAPI 成功 - [fail.html](file:///e:/goproject/goproject/PaymentCenter/front/templates/fail.html) — 支付失败 前端注意:`payPage.html` 中的 `API_BASE_URL` 默认写死为 `http://localhost:7081`,部署时需要替换为生产地址。 --- ## 7. 请求加解密体系 ### 7.1 两个核心中间件 | 中间件 | 路由 | 说明 | |--------|------|------| | `ValidateRequest()` | 收银台列表、授权、小程序等 | 普通参数解析(form/json)+ validator 校验 + 可选日志 | | `ValidatePayRequest()` | `/pay/url`、`/pay/refund`、`/pay/query`、`/pay/close` | 支付专用:`app_id` 查表 → 按 `key_type` 解密 data → 二次解析校验 | 中间件代码:[app/http/middlewares/base.go](file:///e:/goproject/goproject/PaymentCenter/app/http/middlewares/base.go) ### 7.2 支付请求结构(加密封装) ``` { "app_id": 123, // 应用 ID,查表得 KeyType "timestamp": 1700000000, // 时间戳 "data": "...", // 真实业务参数的密文(PayReqs JSON → 加密 → Base64) "key": "..." // 部分算法所需对称密钥密文 } ``` 解密后 `data` 对应的业务结构,在 `requestmapping.FrontRequestMap` 中按路由映射。 ### 7.3 加密算法(由 `app.key_type` 决定) | 类型 | 实现文件 | |------|---------| | NO_CRYPT | 明文(不加密) | | RSA | [services/apicrypt/rsa.go](file:///e:/goproject/goproject/PaymentCenter/app/services/apicrypt/rsa.go) | | SM2 | [services/apicrypt/sm2.go](file:///e:/goproject/goproject/PaymentCenter/app/services/apicrypt/sm2.go) | | SM4 | [services/apicrypt/sm4.go](file:///e:/goproject/goproject/PaymentCenter/app/services/apicrypt/sm4.go) | 注册处:[services/apicrypt/types.go](file:///e:/goproject/goproject/PaymentCenter/app/services/apicrypt/types.go) 中的 `ApiCryptMap` ### 7.4 响应加密 - 非生产环境:`controllers.Success/Error` 直接返回 `{ code, message, data }` JSON - 生产环境(`config.GetConf().Env == "production"`):整体 JSON 用 DES3 + Base64 加密后以纯文本返回 - 对应方法:`controllers.EncriptJson()` --- ## 8. 订单模型 & 状态机 ### 8.1 Orders 表 字段定义:[app/models/ordersmodel/orders.go](file:///e:/goproject/goproject/PaymentCenter/app/models/ordersmodel/orders.go) | 字段 | 类型 | 说明 | |------|------|------| | id | int64 | 主键,snowflake 生成 | | merchant_id | int64 | 商户 | | pay_channel_id | int64 | 支付渠道实例 | | app_id | int64 | 应用 | | out_trade_no | string(50) | 外部/商户订单号 | | order_type | tinyint | 1=支付, 2=退款 | | refund_order_id | int64 | 退款关联的原订单 | | amount | int | 订单金额,单位:**分** | | actual_amount | int | 实际金额,单位:**分** | | payer_total | int | 付款方金额,单位:**分** | | ext_json | string(1024) | 扩展 JSON | | desc | string(100) | 商品描述 | | status | tinyint | 订单状态(见下) | | create_time / update_time | datetime | 自动维护 | ### 8.2 订单状态(`constants/common/common.go`) | 常量 | 值 | 说明 | |------|----|------| | ORDER_STATUS_WAITPAY | 1 | 待支付(刚创建) | | ORDER_STATUS_PAYING | 2 | 支付中(已调第三方下单) | | ORDER_STATUS_PAYED | 3 | 支付成功 | | ORDER_STATUS_FAILED | 4 | 支付失败 | | ORDER_STATUS_CLOSE | 5 | 已关闭 | ### 8.3 状态流转 ``` WAITPAY(1) ──► PAYING(2) ──┬──► PAYED(3) │ │ └─────► CLOSE(5) └──► FAILED(4) ``` 状态检查方法:`services.OrderStatusCheck(order)`,位置 [app/services/pay_page.go](file:///e:/goproject/goproject/PaymentCenter/app/services/pay_page.go#L89-L103) --- ## 9. 支付渠道体系 ### 9.1 渠道类型枚举 定义在 `constants/common/common.go`: | 常量 | 值 | 含义 | |------|----|------| | PAY_CHANNEL_WECHAT_JSAPI | 1 | 微信公众号 JSAPI | | PAY_CHANNEL_WECHAT_H5 | 2 | 微信 H5 | | PAY_CHANNEL_WECHAT_APP | 3 | 微信 APP | | PAY_CHANNEL_WECHAT_NATIVE | 4 | 微信 Native 扫码 | | PAY_CHANNEL_WECHAT_MINI | 5 | 微信小程序 | | PAY_CHANNEL_ALIPAY_WEB | 6 | 支付宝网页/移动应用 | | PAY_CHANNEL_ALIPAY_MINI | 7 | 支付宝小程序 | | PAY_CHANNEL_ALIPAY_JSAPI | 8 | 支付宝 JSAPI | | PAY_CHANNEL_ALIPAY_PC | 9 | 支付宝 PC 网站 | ### 9.2 策略映射(渠道 → 实现方法) - 下单:`services/thirdpay/do/pay_way.go` 的 `PayWayList[channelType]` - 退款:`services/thirdpay/do/refund_way.go` 的 `RefundWayList[channelType]` - 底层统一入口:`third/paymentService/payment_service.go` 的 `PaymentService(ctx, req)` - 根据 `req.ChannelType` 路由到 `AliPayService()` 或 `WechatPayService()` ### 9.3 客户端环境 → 可用渠道 - 微信浏览器内 → JSAPI(需要 openId,收银台会自动先走授权) - PC 浏览器 → 微信 Native、支付宝 PC - 手机浏览器(非微信内)→ 微信 H5、支付宝 Web 实现:`services.PayPageChannelList()` + `services.ClientEnvCheck(ua)` --- ## 10. 新增功能的操作清单 ### 10.1 新增一个 C 端 API 1. 在 `app/http/entities/front/` 定义 Request/Response 结构体 2. 在 `app/http/requestmapping/front.go` 的两个 map 中添加路由 → 结构体映射 3. 在 `app/http/routes/route.go` 注册路由,挂上合适的中间件 4. 在 `app/http/controllers/front/` 对应文件新增 handler(用 `controllers.GetRequest(c).(*front.XXX)` 取参数) 5. 在 `app/services/` 或 `app/services/thirdpay/` 实现业务逻辑 ### 10.2 新增一个支付渠道 1. 在 `constants/common/common.go` 增加 `PAY_CHANNEL_XXX` 常量和 `PayChannelName`/`PayChannelList` 条目 2. 在 `services/thirdpay/do/pay_way.go` 的 `PayWayList` 注册实现方法 3. 在 `third/paymentService/wechat_service.go` 或 `ali_service.go` 增加对应分支 4. 若需区分客户端环境,更新 `OpenInPayChannelMap` ### 10.3 新增一个页面模板 1. 在 `front/templates/` 下新增 `.html` 2. 路由里通过 `c.HTML(http.StatusOK, "文件名.html", gin.H{...})` 渲染 3. `router.LoadHTMLGlob("./front/templates/*")` 已在 routes 中注册 ### 10.4 新增一个错误码 1. 在 `constants/errorcode/error_code.go` 定义 const,并在 `ErrCodeMap` 注册中文消息 2. 使用:`controllers.Error(c, errorcode.XXX)` 或 `controllers.ApiRes(c, nil, code)` ### 10.5 新增数据模型 / Repository 1. 在 `app/models/xxxmodel/` 定义结构体(xorm tag + TableName + GetInstance 单例) 2. 在 `app/data/` 新增 `XxxRepo` 封装 xorm 操作(推荐配合 `builder.Cond` 拼装条件) 3. 在 `app/services/` 新增 `XxxFindOne` / `XxxList` 等服务方法 --- ## 11. 编码规范与注意事项 - **金额单位**:全链路使用 **分**(int);前端展示时 `/100` 转元;不得出现 float64 金额 - **订单 ID vs out_trade_no**: - `orders.id` = 雪花自增 ID(支付中心内部、收银台查询使用) - `out_trade_no` = 商户外部订单号(商户下单、查询使用) - **不要混用** - **幂等性**:V2 下单通过 Redis 分布式锁(key=`payUrl:{out_trade_no}`,3 秒)+ 数据库 `(out_trade_no, app_id)` 唯一约束保证 - **中间件选型**: - 加密支付接口 → `ValidatePayRequest()` - 收银台、授权、小程序等 → `ValidateRequest()` - 回调接口 → 直接在 controller 解析(走第三方验签) - **统一返回**:`controllers.Success/Error/ApiRes/HandCodeRes`,不要手写 JSON - **错误码来源**:`constants/errorcode/error_code.go`,不要硬编码数字 - **日志**:`utils.Log(ctx, tag, ...)`;支付链路关键节点必须有日志 - **单元测试**:工具类、加解密、snowflake 等均有 `_test.go`,新工具函数建议配套 --- ## 12. 关键文件速查 | 功能 | 文件 | |------|------| | 路由注册 | [app/http/routes/route.go](file:///e:/goproject/goproject/PaymentCenter/app/http/routes/route.go) | | 路由→结构体映射 | [app/http/requestmapping/front.go](file:///e:/goproject/goproject/PaymentCenter/app/http/requestmapping/front.go) | | 中间件 | [app/http/middlewares/base.go](file:///e:/goproject/goproject/PaymentCenter/app/http/middlewares/base.go) | | 公共返回/参数解析 | [app/http/controllers/base.go](file:///e:/goproject/goproject/PaymentCenter/app/http/controllers/base.go) | | 支付接口 controller | [app/http/controllers/front/api.go](file:///e:/goproject/goproject/PaymentCenter/app/http/controllers/front/api.go) | | 收银台 controller | [app/http/controllers/front/pay_page.go](file:///e:/goproject/goproject/PaymentCenter/app/http/controllers/front/pay_page.go) | | 回调/微信/支付宝 controller | [app/http/controllers/front/payment_controller.go](file:///e:/goproject/goproject/PaymentCenter/app/http/controllers/front/payment_controller.go) | | 请求结构体 | [app/http/entities/front/pay.go](file:///e:/goproject/goproject/PaymentCenter/app/http/entities/front/pay.go) | | 订单服务 | [app/services/order.go](file:///e:/goproject/goproject/PaymentCenter/app/services/order.go) | | 收银台服务(渠道列表/状态检查) | [app/services/pay_page.go](file:///e:/goproject/goproject/PaymentCenter/app/services/pay_page.go) | | App 校验 | [app/services/api_request_valid.go](file:///e:/goproject/goproject/PaymentCenter/app/services/api_request_valid.go) | | 第三方支付主流程 | [app/services/thirdpay/pay.go](file:///e:/goproject/goproject/PaymentCenter/app/services/thirdpay/pay.go) | | 收银台/V2 下单 | [app/services/thirdpay/pay_page.go](file:///e:/goproject/goproject/PaymentCenter/app/services/thirdpay/pay_page.go) | | 加解密类型注册 | [app/services/apicrypt/types.go](file:///e:/goproject/goproject/PaymentCenter/app/services/apicrypt/types.go) | | 错误码 | [app/constants/errorcode/error_code.go](file:///e:/goproject/goproject/PaymentCenter/app/constants/errorcode/error_code.go) | | 公共常量 | [app/constants/common/common.go](file:///e:/goproject/goproject/PaymentCenter/app/constants/common/common.go) | | Orders 模型 | [app/models/ordersmodel/orders.go](file:///e:/goproject/goproject/PaymentCenter/app/models/ordersmodel/orders.go) | | Order Repository | [app/data/orders.go](file:///e:/goproject/goproject/PaymentCenter/app/data/orders.go) | | 底层 go-pay 调用入口 | app/third/paymentService/payment_service.go | | 收银台页面 | [front/templates/payPage.html](file:///e:/goproject/goproject/PaymentCenter/front/templates/payPage.html) | | 跳转模板 | [front/templates/payTemplateDefault.html](file:///e:/goproject/goproject/PaymentCenter/front/templates/payTemplateDefault.html) | | 雪花 ID 生成 | [app/utils/snowflake/snow_flake.go](file:///e:/goproject/goproject/PaymentCenter/app/utils/snowflake/snow_flake.go) |