Files
junhong_cmp_fiber/internal/routes/payment_merchant.go
break 5ed6b39deb
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat(收口): 补齐 8 月迭代缺口并同步 Spec 与证据链
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕
- 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败
- 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影
- 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序
- 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列
- 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名
- 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
2026-09-18 15:34:29 +08:00

55 lines
7.6 KiB
Go
Raw 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.
package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
)
// requirePaymentMerchantAccess 校验当前用户是否有权访问支付商户配置,
// 仅允许超级管理员和平台用户,其他角色直接返回无权限错误。
func requirePaymentMerchantAccess(handler fiber.Handler) fiber.Handler {
return func(c *fiber.Ctx) error {
kind := middleware.GetUserTypeFromContext(c.UserContext())
if kind != constants.UserTypeSuperAdmin && kind != constants.UserTypePlatform {
return errors.New(errors.CodeForbidden, "无权限访问支付商户配置")
}
return handler(c)
}
}
// paymentMerchantCredentialDoc 说明商户 credentials 允许的键,与 validateMerchantConfiguration 的校验口径一致。
// credentials 是 JSONB生成的 OpenAPI 只能给它一个通用 object 模式,因此键枚举写进操作说明供前端枚举。
const paymentMerchantCredentialDoc = `商户凭证 credentials 为扁平 JSON 对象,键名与渠道配置字段一致;必填键由 payment_method 与 provider_type 组合决定:
- payment_method=wechat、provider_type=wechatwx_mch_id、wx_api_v3_key、wx_cert_content、wx_key_content、wx_serial_no、wx_notify_url
- payment_method=wechat、provider_type=wechat_v2wx_mch_id、wx_api_v2_key、wx_notify_url
- payment_method=wechat、provider_type=fuioufy_mchnt_cd、fy_ins_cd、fy_term_id、fy_private_key、fy_public_key、fy_api_url、fy_notify_url
- payment_method=alipay、provider_type=alipayali_app_id、ali_private_key、ali_public_key、ali_notify_url、ali_return_url
可选键:微信商户可附 wx_api_v2_key微信 v2 商户可附 wx_client_cert_content、wx_client_key_contentAPI 客户端证书与私钥,内容为 PEM 文本);支付宝商户可附 ali_production布尔是否生产环境与 ali_pay_expire_minutes整数支付过期分钟数
除 ali_production 与 ali_pay_expire_minutes 外凭证值必须为字符串merchant_identity 必须分别等于 wx_mch_id、fy_mchnt_cd 或 ali_app_id。
微信 v2 商户的支付与查单只需要 wx_api_v2_key原路退款接口为双向证书接口缺少 wx_client_cert_content 与 wx_client_key_content 时该商户的原路退款按凭证不完整判定为不可用,补录后即可用。
商户凭证为敏感信息,仅超级管理员与平台用户可读可写,日志、审计与支付快照不保存凭证内容。`
func registerPaymentMerchantRoutes(router fiber.Router, handler *admin.PaymentMerchantHandler, doc *openapi.Generator, basePath string) {
group := router
wrap := requirePaymentMerchantAccess
Register(group, doc, basePath, "GET", "/payment-merchants", wrap(handler.ListMerchants), RouteSpec{Summary: "查询支付商户", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"支付商户管理"}, Input: new(dto.PaymentMerchantListRequest), Output: new(dto.PaymentMerchantResponse), Auth: true})
Register(group, doc, basePath, "POST", "/payment-merchants", wrap(handler.CreateMerchant), RouteSpec{Summary: "创建支付商户", Description: "仅超级管理员和平台用户可访问。\n\n" + paymentMerchantCredentialDoc, Tags: []string{"支付商户管理"}, Input: new(dto.PaymentMerchantRequest), Output: new(dto.PaymentMerchantResponse), Auth: true})
Register(group, doc, basePath, "GET", "/payment-merchants/:id", wrap(handler.GetMerchant), RouteSpec{Summary: "查询支付商户详情", Description: "仅超级管理员和平台用户可访问。\n\n" + paymentMerchantCredentialDoc, Tags: []string{"支付商户管理"}, Input: new(dto.IDReq), Output: new(dto.PaymentMerchantResponse), Auth: true})
Register(group, doc, basePath, "PUT", "/payment-merchants/:id", wrap(handler.UpdateMerchant), RouteSpec{Summary: "更新支付商户", Description: "仅超级管理员和平台用户可访问。\n\n" + paymentMerchantCredentialDoc, Tags: []string{"支付商户管理"}, Input: new(dto.IDReq), Body: new(dto.PaymentMerchantUpdateRequest), Output: new(dto.PaymentMerchantResponse), Auth: true})
Register(group, doc, basePath, "DELETE", "/payment-merchants/:id", wrap(handler.DeleteMerchant), RouteSpec{Summary: "删除支付商户", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"支付商户管理"}, Input: new(dto.IDReq), Body: new(dto.PaymentMerchantDeleteRequest), Output: nil, Auth: true})
Register(group, doc, basePath, "GET", "/payment-merchant-pools", wrap(handler.ListPools), RouteSpec{Summary: "查询商户池", Description: "仅超级管理员和平台用户可访问。支持分页,返回当前页商户池及其有序成员,并附带启用成员数量、成员总数量、当前命中商户与最近配置更新时间;当前命中商户按支付创建的同一规则只读计算,无可用成员时为空。", Tags: []string{"商户池管理"}, Input: new(dto.PaymentMerchantPoolListRequest), Output: new(dto.PaymentMerchantPoolResponse), Auth: true})
Register(group, doc, basePath, "GET", "/payment-merchant-pools/:id", wrap(handler.GetPool), RouteSpec{Summary: "查询商户池详情", Description: "仅超级管理员和平台用户可访问。返回启用成员数量、成员总数量、当前命中商户与最近配置更新时间;当前命中商户只读计算,不产生支付单也不推进统计世代。", Tags: []string{"商户池管理"}, Input: new(dto.IDReq), Output: new(dto.PaymentMerchantPoolResponse), Auth: true})
Register(group, doc, basePath, "POST", "/payment-merchant-pools", wrap(handler.CreatePool), RouteSpec{Summary: "创建商户池", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"商户池管理"}, Input: new(dto.PaymentMerchantPoolRequest), Output: new(dto.PaymentMerchantPoolResponse), Auth: true})
Register(group, doc, basePath, "PUT", "/payment-merchant-pools/:id", wrap(handler.UpdatePool), RouteSpec{Summary: "更新商户池", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"商户池管理"}, Input: new(dto.IDReq), Body: new(dto.PaymentMerchantPoolRequest), Output: new(dto.PaymentMerchantPoolResponse), Auth: true})
Register(group, doc, basePath, "POST", "/payment-merchant-pools/:id/enable", wrap(handler.EnablePool), RouteSpec{Summary: "启用商户池", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"商户池管理"}, Input: new(dto.IDReq), Output: new(dto.PaymentMerchantPoolResponse), Auth: true})
Register(group, doc, basePath, "POST", "/payment-merchant-pools/:id/disable", wrap(handler.DisablePool), RouteSpec{Summary: "停用商户池", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"商户池管理"}, Input: new(dto.IDReq), Output: new(dto.PaymentMerchantPoolResponse), Auth: true})
Register(group, doc, basePath, "GET", "/wechat-authorizations", wrap(handler.GetAuthorization), RouteSpec{Summary: "获取微信授权配置", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"微信授权配置"}, Output: new(dto.WechatAuthorizationResponse), Auth: true})
Register(group, doc, basePath, "PUT", "/wechat-authorizations/current", wrap(handler.SaveAuthorization), RouteSpec{Summary: "保存微信授权配置", Description: "仅超级管理员和平台用户可访问。", Tags: []string{"微信授权配置"}, Input: new(dto.WechatAuthorizationRequest), Output: new(dto.WechatAuthorizationResponse), Auth: true})
}