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=wechat:wx_mch_id、wx_api_v3_key、wx_cert_content、wx_key_content、wx_serial_no、wx_notify_url - payment_method=wechat、provider_type=wechat_v2:wx_mch_id、wx_api_v2_key、wx_notify_url - payment_method=wechat、provider_type=fuiou:fy_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=alipay:ali_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_content(API 客户端证书与私钥,内容为 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}) }