实现支付商户池与微信授权配置
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 3m55s

新增收款商户、商户池轮询、微信授权配置独立管理;三类新支付
(C端套餐购买、C端资产钱包充值、代理在线预存款充值)无条件
经商户池选择并冻结路由,无旧综合配置回退。merchant_id 为空
历史支付继续按 payment_config_id 双读。凭证版本化加载与
ID+版本缓存保证轮换一致性。删除商户池新支付创建开关及全部
引用。
This commit is contained in:
2026-09-09 18:13:04 +08:00
parent ff25586dc9
commit 98c145fe70
39 changed files with 2718 additions and 415 deletions

View File

@@ -0,0 +1,124 @@
package dto
import (
"time"
"github.com/break/junhong_cmp_fiber/internal/model"
)
// PaymentMerchantRequest 是受控商户管理写入请求。
type PaymentMerchantRequest struct {
Name string `json:"name" validate:"required,min=1,max=100" description:"商户名称"`
PaymentMethod string `json:"payment_method" validate:"required,oneof=wechat alipay" enum:"wechat,alipay" description:"支付方式wechat=微信支付alipay=支付宝支付"`
ProviderType string `json:"provider_type" validate:"required,max=30" description:"服务商类型:微信仅支持 wechat、wechat_v2、fuiou支付宝为 alipay"`
MerchantIdentity string `json:"merchant_identity" validate:"required,max=100" description:"商户号或应用标识"`
Credentials model.JSONB `json:"credentials" description:"商户受控凭证,仅专用管理接口传输"`
Enabled bool `json:"enabled" description:"是否启用"`
Remark string `json:"remark" validate:"max=1000" description:"备注"`
}
// PaymentMerchantUpdateRequest 是受控商户管理更新请求。
type PaymentMerchantUpdateRequest struct {
Name *string `json:"name" description:"商户名称"`
PaymentMethod *string `json:"payment_method" enum:"wechat,alipay" description:"支付方式wechat=微信支付alipay=支付宝支付"`
ProviderType *string `json:"provider_type" description:"服务商类型:微信仅支持 wechat、wechat_v2、fuiou支付宝为 alipay"`
MerchantIdentity *string `json:"merchant_identity" description:"商户号或应用标识"`
Credentials *model.JSONB `json:"credentials" description:"商户受控凭证"`
Enabled *bool `json:"enabled" description:"是否启用"`
Remark *string `json:"remark" description:"备注"`
}
// PaymentMerchantDeleteRequest 是受控商户删除二次确认请求。
type PaymentMerchantDeleteRequest struct {
Confirm bool `json:"confirm" validate:"required" description:"确认删除必须为true"`
}
// PaymentMerchantListRequest 是支付商户分页查询条件。
type PaymentMerchantListRequest struct {
Page int `query:"page" description:"页码"`
PageSize int `query:"page_size" description:"每页数量"`
PaymentMethod *string `query:"payment_method" enum:"wechat,alipay" description:"支付方式wechat=微信支付alipay=支付宝支付"`
Enabled *bool `query:"enabled" description:"是否启用"`
}
// PaymentMerchantResponse 是受控商户管理响应。
type PaymentMerchantResponse struct {
ID uint `json:"id" description:"商户ID"`
Name string `json:"name" description:"商户名称"`
PaymentMethod string `json:"payment_method" enum:"wechat,alipay" description:"支付方式wechat=微信支付alipay=支付宝支付"`
ProviderType string `json:"provider_type" description:"服务商类型"`
MerchantIdentity string `json:"merchant_identity" description:"商户号或应用标识"`
Credentials model.JSONB `json:"credentials" description:"商户受控凭证,仅专用管理接口返回"`
CredentialVersion int64 `json:"credential_version" description:"凭证版本"`
Enabled bool `json:"enabled" description:"是否启用"`
Remark string `json:"remark" description:"备注"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
}
// PaymentMerchantPoolRequest 是商户池创建或更新请求。
type PaymentMerchantPoolRequest struct {
Name string `json:"name" validate:"required,min=1,max=100" description:"商户池名称"`
PaymentMethod string `json:"payment_method" validate:"required,oneof=wechat alipay" enum:"wechat,alipay" description:"支付方式wechat=微信支付alipay=支付宝支付"`
Enabled bool `json:"enabled" description:"是否启用"`
Strategy string `json:"strategy" validate:"required,oneof=amount count time" enum:"amount,count,time" description:"轮询策略amount=金额阈值count=笔数阈值time=按时间段轮换"`
ThresholdAmount *int64 `json:"threshold_amount" description:"金额轮询阈值,单位分;仅 amount 策略"`
ThresholdCount *int64 `json:"threshold_count" description:"笔数轮询阈值;仅 count 策略"`
StatisticCycle *string `json:"statistic_cycle" enum:"round,day,month" description:"金额/笔数统计周期round=每轮day=自然日month=自然月"`
TimePeriodValue *int64 `json:"time_period_value" description:"时间轮询周期数值;仅 time 策略,最小 1"`
TimePeriodUnit *string `json:"time_period_unit" enum:"minute,hour,day" description:"时间轮询单位minute=分钟hour=小时day=天"`
TimePeriodStartedAt *time.Time `json:"time_period_started_at" description:"时间轮询起始时间;仅 time 策略"`
MemberIDs []uint `json:"member_ids" validate:"required,min=1" description:"有序商户ID列表"`
Remark string `json:"remark" description:"备注"`
}
// PaymentMerchantPoolListRequest 是商户池分页查询条件。
type PaymentMerchantPoolListRequest struct {
Page int `query:"page" description:"页码"`
PageSize int `query:"page_size" description:"每页数量"`
}
// PaymentMerchantPoolResponse 是商户池管理响应。
type PaymentMerchantPoolResponse struct {
ID uint `json:"id" description:"商户池ID"`
Name string `json:"name" description:"商户池名称"`
PaymentMethod string `json:"payment_method" enum:"wechat,alipay" description:"支付方式wechat=微信支付alipay=支付宝支付"`
Enabled bool `json:"enabled" description:"是否启用"`
Strategy string `json:"strategy" enum:"amount,count,time" description:"轮询策略amount=金额阈值count=笔数阈值time=按时间段轮换"`
ThresholdAmount *int64 `json:"threshold_amount,omitempty" description:"金额阈值,分"`
ThresholdCount *int64 `json:"threshold_count,omitempty" description:"笔数阈值"`
StatisticCycle *string `json:"statistic_cycle,omitempty" enum:"round,day,month" description:"统计周期round=每轮day=自然日month=自然月"`
TimePeriodValue *int64 `json:"time_period_value,omitempty" description:"时间周期数值"`
TimePeriodUnit *string `json:"time_period_unit,omitempty" enum:"minute,hour,day" description:"时间单位minute=分钟hour=小时day=天"`
TimePeriodStartedAt *time.Time `json:"time_period_started_at,omitempty" description:"时间轮询起始时间"`
RoutingEpoch int64 `json:"routing_epoch" description:"当前路由统计世代"`
MemberIDs []uint `json:"member_ids" description:"有序商户ID列表"`
Remark string `json:"remark" description:"备注"`
}
// WechatAuthorizationRequest 是微信授权配置管理写入请求。
type WechatAuthorizationRequest struct {
OaAppID string `json:"oa_app_id" description:"公众号AppID"`
OaAppSecret string `json:"oa_app_secret" description:"公众号AppSecret"`
OaToken string `json:"oa_token" description:"公众号Token"`
OaAesKey string `json:"oa_aes_key" description:"公众号AES密钥"`
OaOAuthRedirectURL string `json:"oa_oauth_redirect_url" description:"公众号OAuth回调地址"`
MiniappAppID string `json:"miniapp_app_id" description:"小程序AppID"`
MiniappAppSecret string `json:"miniapp_app_secret" description:"小程序AppSecret"`
Enabled bool `json:"enabled" description:"是否启用"`
}
// WechatAuthorizationResponse 是微信授权配置管理响应。
type WechatAuthorizationResponse struct {
ID uint `json:"id" description:"授权配置ID"`
OaAppID string `json:"oa_app_id" description:"公众号AppID"`
OaAppSecret string `json:"oa_app_secret" description:"公众号AppSecret仅专用管理接口返回"`
OaToken string `json:"oa_token" description:"公众号Token仅专用管理接口返回"`
OaAesKey string `json:"oa_aes_key" description:"公众号AES密钥仅专用管理接口返回"`
OaOAuthRedirectURL string `json:"oa_oauth_redirect_url" description:"公众号OAuth回调地址"`
MiniappAppID string `json:"miniapp_app_id" description:"小程序AppID"`
MiniappAppSecret string `json:"miniapp_app_secret" description:"小程序AppSecret仅专用管理接口返回"`
CredentialVersion int64 `json:"credential_version" description:"凭证版本"`
Enabled bool `json:"enabled" description:"是否启用"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
}

View File

@@ -8,23 +8,31 @@ import (
)
type Payment struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
PaymentNo string `gorm:"column:payment_no;type:varchar(40);uniqueIndex;not null" json:"payment_no"`
OrderID uint `gorm:"column:order_id;not null" json:"order_id"`
OrderType string `gorm:"column:order_type;type:varchar(30);not null" json:"order_type"`
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);not null" json:"payment_method"`
MerchantIdentity string `gorm:"column:merchant_identity;type:varchar(100)" json:"-"`
Amount int64 `gorm:"column:amount;type:bigint;not null" json:"amount"`
Status int `gorm:"column:status;type:smallint;not null;default:0" json:"status"`
ThirdPartyTradeNo string `gorm:"column:third_party_trade_no;type:varchar(100)" json:"third_party_trade_no,omitempty"`
PaymentConfigID *uint `gorm:"column:payment_config_id" json:"payment_config_id,omitempty"`
PaymentVoucherKey string `gorm:"column:payment_voucher_key;type:varchar(500)" json:"payment_voucher_key,omitempty"`
QRContent string `gorm:"column:qr_content;type:text" json:"-"`
PaidAt *time.Time `gorm:"column:paid_at" json:"paid_at,omitempty"`
ExpireAt *time.Time `gorm:"column:expire_at" json:"expire_at,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
ID uint `gorm:"column:id;primaryKey" json:"id"`
PaymentNo string `gorm:"column:payment_no;type:varchar(40);uniqueIndex;not null" json:"payment_no"`
OrderID uint `gorm:"column:order_id;not null" json:"order_id"`
OrderType string `gorm:"column:order_type;type:varchar(30);not null" json:"order_type"`
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);not null" json:"payment_method"`
MerchantIdentity string `gorm:"column:merchant_identity;type:varchar(100)" json:"-"`
MerchantID *uint `gorm:"column:merchant_id" json:"merchant_id,omitempty"`
MerchantPoolID *uint `gorm:"column:merchant_pool_id" json:"merchant_pool_id,omitempty"`
MerchantNameSnapshot string `gorm:"column:merchant_name_snapshot" json:"merchant_name_snapshot,omitempty"`
MerchantPaymentMethodSnapshot string `gorm:"column:merchant_payment_method_snapshot" json:"merchant_payment_method_snapshot,omitempty"`
MerchantProviderTypeSnapshot string `gorm:"column:merchant_provider_type_snapshot" json:"merchant_provider_type_snapshot,omitempty"`
MerchantPoolNameSnapshot string `gorm:"column:merchant_pool_name_snapshot" json:"merchant_pool_name_snapshot,omitempty"`
RoutingStrategySnapshot string `gorm:"column:routing_strategy_snapshot" json:"routing_strategy_snapshot,omitempty"`
RoutingEpoch *int64 `gorm:"column:routing_epoch" json:"routing_epoch,omitempty"`
Amount int64 `gorm:"column:amount;type:bigint;not null" json:"amount"`
Status int `gorm:"column:status;type:smallint;not null;default:0" json:"status"`
ThirdPartyTradeNo string `gorm:"column:third_party_trade_no;type:varchar(100)" json:"third_party_trade_no,omitempty"`
PaymentConfigID *uint `gorm:"column:payment_config_id" json:"payment_config_id,omitempty"`
PaymentVoucherKey string `gorm:"column:payment_voucher_key;type:varchar(500)" json:"payment_voucher_key,omitempty"`
QRContent string `gorm:"column:qr_content;type:text" json:"-"`
PaidAt *time.Time `gorm:"column:paid_at" json:"paid_at,omitempty"`
ExpireAt *time.Time `gorm:"column:expire_at" json:"expire_at,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
func (Payment) TableName() string {

View File

@@ -0,0 +1,94 @@
package model
import (
"time"
"gorm.io/gorm"
)
const (
PaymentMerchantStatusDisabled = 0
PaymentMerchantStatusEnabled = 1
PaymentMerchantStrategyAmount = "amount"
PaymentMerchantStrategyCount = "count"
PaymentMerchantStrategyTime = "time"
)
// PaymentMerchant 是实际收款商户,凭证只保存在受控字段。
type PaymentMerchant struct {
gorm.Model
BaseModel `gorm:"embedded"`
Name string `gorm:"column:name" json:"name"`
PaymentMethod string `gorm:"column:payment_method" json:"payment_method"`
ProviderType string `gorm:"column:provider_type" json:"provider_type"`
MerchantIdentity string `gorm:"column:merchant_identity" json:"merchant_identity"`
Credentials JSONB `gorm:"column:credentials;type:jsonb" json:"-"`
CredentialVersion int64 `gorm:"column:credential_version" json:"credential_version"`
Status int `gorm:"column:status" json:"status"`
Remark string `gorm:"column:remark" json:"remark"`
}
func (PaymentMerchant) TableName() string { return "tb_payment_merchant" }
// PaymentMerchantPool 是一种支付方式的商户轮询池。
type PaymentMerchantPool struct {
gorm.Model
BaseModel `gorm:"embedded"`
Name string `gorm:"column:name" json:"name"`
PaymentMethod string `gorm:"column:payment_method" json:"payment_method"`
Status int `gorm:"column:status" json:"status"`
Strategy string `gorm:"column:strategy" json:"strategy"`
ThresholdAmount *int64 `gorm:"column:threshold_amount" json:"threshold_amount,omitempty"`
ThresholdCount *int64 `gorm:"column:threshold_count" json:"threshold_count,omitempty"`
StatisticCycle *string `gorm:"column:statistic_cycle" json:"statistic_cycle,omitempty"`
TimePeriodValue *int64 `gorm:"column:time_period_value" json:"time_period_value,omitempty"`
TimePeriodUnit *string `gorm:"column:time_period_unit" json:"time_period_unit,omitempty"`
TimePeriodStartedAt *time.Time `gorm:"column:time_period_started_at" json:"time_period_started_at,omitempty"`
RoutingEpoch int64 `gorm:"column:routing_epoch" json:"routing_epoch"`
Remark string `gorm:"column:remark" json:"remark"`
}
func (PaymentMerchantPool) TableName() string { return "tb_payment_merchant_pool" }
// PaymentMerchantPoolMember 是商户池内按顺序参与轮询的商户。
type PaymentMerchantPoolMember struct {
gorm.Model
BaseModel `gorm:"embedded"`
PoolID uint `gorm:"column:pool_id" json:"pool_id"`
MerchantID uint `gorm:"column:merchant_id" json:"merchant_id"`
SortOrder int64 `gorm:"column:sort_order" json:"sort_order"`
}
func (PaymentMerchantPoolMember) TableName() string { return "tb_payment_merchant_pool_member" }
// WechatAuthorization 是 C 端微信 OAuth、小程序与支付 AppID 的全局授权配置。
type WechatAuthorization struct {
gorm.Model
BaseModel `gorm:"embedded"`
OaAppID string `gorm:"column:oa_app_id" json:"oa_app_id"`
OaAppSecret string `gorm:"column:oa_app_secret" json:"-"`
OaToken string `gorm:"column:oa_token" json:"-"`
OaAesKey string `gorm:"column:oa_aes_key" json:"-"`
OaOAuthRedirectURL string `gorm:"column:oa_oauth_redirect_url" json:"oa_oauth_redirect_url"`
MiniappAppID string `gorm:"column:miniapp_app_id" json:"miniapp_app_id"`
MiniappAppSecret string `gorm:"column:miniapp_app_secret" json:"-"`
CredentialVersion int64 `gorm:"column:credential_version" json:"credential_version"`
Status int `gorm:"column:status" json:"status"`
}
func (WechatAuthorization) TableName() string { return "tb_wechat_authorization" }
// PaymentMerchantRoutingSuccess 是支付首次成功的唯一统计事实。
type PaymentMerchantRoutingSuccess struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
PaymentID uint `gorm:"column:payment_id" json:"payment_id"`
MerchantID uint `gorm:"column:merchant_id" json:"merchant_id"`
PoolID uint `gorm:"column:pool_id" json:"pool_id"`
RoutingEpoch int64 `gorm:"column:routing_epoch" json:"routing_epoch"`
Amount int64 `gorm:"column:amount" json:"amount"`
PaidAt time.Time `gorm:"column:paid_at" json:"paid_at"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
}
func (PaymentMerchantRoutingSuccess) TableName() string { return "tb_payment_merchant_routing_success" }