|
|
||
|---|---|---|
| app | ||
| bootstrap | ||
| build | ||
| config | ||
| docs | ||
| event | ||
| front/templates | ||
| rpc | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| Makefile | ||
| README.md | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
README.md
PaymentCenter 支付中心
一个基于 Go + Gin 的统一支付网关系统,为商户提供下单、退款、查询、收银台等支付能力,底层对接微信支付、支付宝等第三方支付平台。
代码位置:后端接口位于
app/,收银台页面模板位于front/templates/。
1. 技术栈
| 组件 | 选型 |
|---|---|
| 语言 | Go |
| Web 框架 | Gin |
| ORM | xorm |
| 第三方支付 | go-pay |
| JSON | 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
- 路由→结构体映射参见 app/http/requestmapping/front.go
5. 两种下单模式
5.1 模式一:V1 直接下单(POST /pay/front/api/v1/pay/url)
适用于商户自己管理收银台页面:
- 商户构造请求
RequestBody{ app_id, timestamp, data, key },加密后发起 - 中间件
ValidatePayRequest():查 App 配置 → 解密 → 反序列化为PayReqs - 控制器调用
thirdpay.ThirdPayInfoCheck()→thirdpay.ThirdPayUrl() - 同步返回
ApiResponse{ Order, Url, JsInfo }
5.2 模式二:V2 预下单 + 收银台(推荐)
适用于商户跳转至我方收银台页面:
- 商户调用
POST /pay/front/api/v2/pay/url 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}
- Redis 分布式锁(
- 商户将用户浏览器跳转到收银台
- 收银台页面(
payPage.html):- 读取 URL 参数
no(订单自增 ID,不是 out_trade_no) POST /payPage/list?id=xxx拉取渠道列表- 单渠道直接跳转
/payPage/submit,多渠道让用户选择 - 选择后跳转到第三方支付页面
- 支付完成后轮询
/payPage/query,成功后跳转到商户return_url
- 读取 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 — 收银台主页(含加载态、渠道选择、状态确认弹窗)
- payTemplateDefault.html — 第三方支付跳转
- success.html — 微信 JSAPI 成功
- 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
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 |
| SM2 | services/apicrypt/sm2.go |
| SM4 | services/apicrypt/sm4.go |
注册处: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
| 字段 | 类型 | 说明 |
|---|---|---|
| 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
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
- 在
app/http/entities/front/定义 Request/Response 结构体 - 在
app/http/requestmapping/front.go的两个 map 中添加路由 → 结构体映射 - 在
app/http/routes/route.go注册路由,挂上合适的中间件 - 在
app/http/controllers/front/对应文件新增 handler(用controllers.GetRequest(c).(*front.XXX)取参数) - 在
app/services/或app/services/thirdpay/实现业务逻辑
10.2 新增一个支付渠道
- 在
constants/common/common.go增加PAY_CHANNEL_XXX常量和PayChannelName/PayChannelList条目 - 在
services/thirdpay/do/pay_way.go的PayWayList注册实现方法 - 在
third/paymentService/wechat_service.go或ali_service.go增加对应分支 - 若需区分客户端环境,更新
OpenInPayChannelMap
10.3 新增一个页面模板
- 在
front/templates/下新增.html - 路由里通过
c.HTML(http.StatusOK, "文件名.html", gin.H{...})渲染 router.LoadHTMLGlob("./front/templates/*")已在 routes 中注册
10.4 新增一个错误码
- 在
constants/errorcode/error_code.go定义 const,并在ErrCodeMap注册中文消息 - 使用:
controllers.Error(c, errorcode.XXX)或controllers.ApiRes(c, nil, code)
10.5 新增数据模型 / Repository
- 在
app/models/xxxmodel/定义结构体(xorm tag + TableName + GetInstance 单例) - 在
app/data/新增XxxRepo封装 xorm 操作(推荐配合builder.Cond拼装条件) - 在
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 |
| 路由→结构体映射 | app/http/requestmapping/front.go |
| 中间件 | app/http/middlewares/base.go |
| 公共返回/参数解析 | app/http/controllers/base.go |
| 支付接口 controller | app/http/controllers/front/api.go |
| 收银台 controller | app/http/controllers/front/pay_page.go |
| 回调/微信/支付宝 controller | app/http/controllers/front/payment_controller.go |
| 请求结构体 | app/http/entities/front/pay.go |
| 订单服务 | app/services/order.go |
| 收银台服务(渠道列表/状态检查) | app/services/pay_page.go |
| App 校验 | app/services/api_request_valid.go |
| 第三方支付主流程 | app/services/thirdpay/pay.go |
| 收银台/V2 下单 | app/services/thirdpay/pay_page.go |
| 加解密类型注册 | app/services/apicrypt/types.go |
| 错误码 | app/constants/errorcode/error_code.go |
| 公共常量 | app/constants/common/common.go |
| Orders 模型 | app/models/ordersmodel/orders.go |
| Order Repository | app/data/orders.go |
| 底层 go-pay 调用入口 | app/third/paymentService/payment_service.go |
| 收银台页面 | front/templates/payPage.html |
| 跳转模板 | front/templates/payTemplateDefault.html |
| 雪花 ID 生成 | app/utils/snowflake/snow_flake.go |