task: 完成支付宝共享基础(迁移/WechatConfig/PaymentStore/pkg/alipay)
This commit is contained in:
@@ -10,7 +10,7 @@ import (
|
||||
type CreateWechatConfigRequest struct {
|
||||
Name string `json:"name" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"配置名称"`
|
||||
Description string `json:"description" validate:"omitempty,max=500" maxLength:"500" description:"配置描述"`
|
||||
ProviderType string `json:"provider_type" validate:"required,oneof=wechat fuiou" required:"true" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
|
||||
ProviderType string `json:"provider_type" validate:"required,oneof=wechat wechat_v2 fuiou" required:"true" description:"支付渠道类型 (wechat:微信直连v3, wechat_v2:微信直连v2, fuiou:富友)"`
|
||||
|
||||
OaAppID string `json:"oa_app_id" validate:"omitempty,max=100" maxLength:"100" description:"公众号AppID"`
|
||||
OaAppSecret string `json:"oa_app_secret" validate:"omitempty,max=200" maxLength:"200" description:"公众号AppSecret"`
|
||||
@@ -36,13 +36,22 @@ type CreateWechatConfigRequest struct {
|
||||
FyPublicKey string `json:"fy_public_key" validate:"omitempty" description:"富友公钥(PEM格式)"`
|
||||
FyAPIURL string `json:"fy_api_url" validate:"omitempty,max=500" maxLength:"500" description:"富友API地址"`
|
||||
FyNotifyURL string `json:"fy_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"富友支付回调地址"`
|
||||
|
||||
// 支付宝配置字段(并存能力,不受 provider_type 限制)
|
||||
AliAppID string `json:"ali_app_id" validate:"omitempty,max=100" maxLength:"100" description:"支付宝应用ID"`
|
||||
AliPrivateKey string `json:"ali_private_key" validate:"omitempty" description:"支付宝应用私钥(PEM格式)"`
|
||||
AliPublicKey string `json:"ali_public_key" validate:"omitempty" description:"支付宝公钥(PEM格式,用于验签)"`
|
||||
AliNotifyURL string `json:"ali_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"支付宝异步通知地址"`
|
||||
AliReturnURL string `json:"ali_return_url" validate:"omitempty,max=500" maxLength:"500" description:"支付宝同步跳转地址"`
|
||||
AliProduction bool `json:"ali_production" description:"是否生产环境 (true:生产, false:沙箱)"`
|
||||
AliPayExpireMinutes int `json:"ali_pay_expire_minutes" validate:"omitempty,min=1,max=1440" minimum:"1" maximum:"1440" description:"支付过期分钟数 (默认30分钟)"`
|
||||
}
|
||||
|
||||
// UpdateWechatConfigRequest 更新微信参数配置请求
|
||||
type UpdateWechatConfigRequest struct {
|
||||
Name *string `json:"name" validate:"omitempty,min=1,max=100" minLength:"1" maxLength:"100" description:"配置名称"`
|
||||
Description *string `json:"description" validate:"omitempty,max=500" maxLength:"500" description:"配置描述"`
|
||||
ProviderType *string `json:"provider_type" validate:"omitempty,oneof=wechat fuiou" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
|
||||
ProviderType *string `json:"provider_type" validate:"omitempty,oneof=wechat wechat_v2 fuiou" description:"支付渠道类型 (wechat:微信直连v3, wechat_v2:微信直连v2, fuiou:富友)"`
|
||||
|
||||
OaAppID *string `json:"oa_app_id" validate:"omitempty,max=100" maxLength:"100" description:"公众号AppID"`
|
||||
OaAppSecret *string `json:"oa_app_secret" validate:"omitempty,max=200" maxLength:"200" description:"公众号AppSecret"`
|
||||
@@ -68,6 +77,15 @@ type UpdateWechatConfigRequest struct {
|
||||
FyPublicKey *string `json:"fy_public_key" validate:"omitempty" description:"富友公钥(PEM格式)"`
|
||||
FyAPIURL *string `json:"fy_api_url" validate:"omitempty,max=500" maxLength:"500" description:"富友API地址"`
|
||||
FyNotifyURL *string `json:"fy_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"富友支付回调地址"`
|
||||
|
||||
// 支付宝配置字段(并存能力,不受 provider_type 限制)
|
||||
AliAppID *string `json:"ali_app_id" validate:"omitempty,max=100" maxLength:"100" description:"支付宝应用ID"`
|
||||
AliPrivateKey *string `json:"ali_private_key" validate:"omitempty" description:"支付宝应用私钥(PEM格式)"`
|
||||
AliPublicKey *string `json:"ali_public_key" validate:"omitempty" description:"支付宝公钥(PEM格式,用于验签)"`
|
||||
AliNotifyURL *string `json:"ali_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"支付宝异步通知地址"`
|
||||
AliReturnURL *string `json:"ali_return_url" validate:"omitempty,max=500" maxLength:"500" description:"支付宝同步跳转地址"`
|
||||
AliProduction *bool `json:"ali_production" description:"是否生产环境 (true:生产, false:沙箱)"`
|
||||
AliPayExpireMinutes *int `json:"ali_pay_expire_minutes" validate:"omitempty,min=1,max=1440" minimum:"1" maximum:"1440" description:"支付过期分钟数"`
|
||||
}
|
||||
|
||||
// UpdateWechatConfigParams 更新微信参数配置聚合参数 (用于文档生成)
|
||||
@@ -80,7 +98,7 @@ type UpdateWechatConfigParams struct {
|
||||
type WechatConfigListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||
ProviderType *string `json:"provider_type" query:"provider_type" validate:"omitempty,oneof=wechat fuiou" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
|
||||
ProviderType *string `json:"provider_type" query:"provider_type" validate:"omitempty,oneof=wechat wechat_v2 fuiou" description:"支付渠道类型 (wechat:微信直连v3, wechat_v2:微信直连v2, fuiou:富友)"`
|
||||
IsActive *bool `json:"is_active" query:"is_active" description:"是否激活 (true:已激活, false:未激活)"`
|
||||
}
|
||||
|
||||
@@ -89,7 +107,7 @@ type WechatConfigResponse struct {
|
||||
ID uint `json:"id" description:"配置ID"`
|
||||
Name string `json:"name" description:"配置名称"`
|
||||
Description string `json:"description" description:"配置描述"`
|
||||
ProviderType string `json:"provider_type" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
|
||||
ProviderType string `json:"provider_type" description:"支付渠道类型 (wechat:微信直连v3, wechat_v2:微信直连v2, fuiou:富友)"`
|
||||
IsActive bool `json:"is_active" description:"是否激活"`
|
||||
|
||||
OaAppID string `json:"oa_app_id" description:"公众号AppID"`
|
||||
@@ -117,6 +135,15 @@ type WechatConfigResponse struct {
|
||||
FyAPIURL string `json:"fy_api_url" description:"富友API地址"`
|
||||
FyNotifyURL string `json:"fy_notify_url" description:"富友支付回调地址"`
|
||||
|
||||
// 支付宝配置字段(敏感字段已脱敏)
|
||||
AliAppID string `json:"ali_app_id" description:"支付宝应用ID"`
|
||||
AliPrivateKey string `json:"ali_private_key" description:"支付宝应用私钥(配置状态)"`
|
||||
AliPublicKey string `json:"ali_public_key" description:"支付宝公钥(配置状态)"`
|
||||
AliNotifyURL string `json:"ali_notify_url" description:"支付宝异步通知地址"`
|
||||
AliReturnURL string `json:"ali_return_url" description:"支付宝同步跳转地址"`
|
||||
AliProduction bool `json:"ali_production" description:"是否生产环境"`
|
||||
AliPayExpireMinutes int `json:"ali_pay_expire_minutes" description:"支付过期分钟数"`
|
||||
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
@@ -180,6 +207,14 @@ func FromWechatConfigModel(m *model.WechatConfig) *WechatConfigResponse {
|
||||
FyAPIURL: m.FyAPIURL,
|
||||
FyNotifyURL: m.FyNotifyURL,
|
||||
|
||||
AliAppID: m.AliAppID,
|
||||
AliPrivateKey: MaskLongSecret(m.AliPrivateKey),
|
||||
AliPublicKey: MaskLongSecret(m.AliPublicKey),
|
||||
AliNotifyURL: m.AliNotifyURL,
|
||||
AliReturnURL: m.AliReturnURL,
|
||||
AliProduction: m.AliProduction,
|
||||
AliPayExpireMinutes: m.AliPayExpireMinutes,
|
||||
|
||||
CreatedAt: m.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: m.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type Payment struct {
|
||||
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"`
|
||||
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"`
|
||||
|
||||
@@ -9,6 +9,9 @@ const (
|
||||
ProviderTypeFuiou = "fuiou" // 富友
|
||||
)
|
||||
|
||||
// 支付宝支付过期默认分钟数
|
||||
const DefaultAliPayExpireMinutes = 30
|
||||
|
||||
// WechatConfig 微信参数配置模型
|
||||
// 管理微信公众号 OAuth、小程序、微信直连支付、富友支付等多套配置
|
||||
// 同一时间只有一条记录处于 is_active=true(全局唯一生效配置)
|
||||
@@ -49,6 +52,15 @@ type WechatConfig struct {
|
||||
FyPublicKey string `gorm:"column:fy_public_key;type:text;default:'';comment:富友公钥" json:"fy_public_key"`
|
||||
FyAPIURL string `gorm:"column:fy_api_url;type:varchar(500);default:'';comment:富友API地址" json:"fy_api_url"`
|
||||
FyNotifyURL string `gorm:"column:fy_notify_url;type:varchar(500);default:'';comment:富友支付回调地址" json:"fy_notify_url"`
|
||||
|
||||
// 支付-支付宝(与微信/富友并存,provider_type 不新增 alipay)
|
||||
AliAppID string `gorm:"column:ali_app_id;type:varchar(100);not null;default:'';comment:支付宝应用ID" json:"ali_app_id"`
|
||||
AliPrivateKey string `gorm:"column:ali_private_key;type:text;not null;default:'';comment:支付宝应用私钥(敏感)" json:"ali_private_key"`
|
||||
AliPublicKey string `gorm:"column:ali_public_key;type:text;not null;default:'';comment:支付宝公钥(用于验签)" json:"ali_public_key"`
|
||||
AliNotifyURL string `gorm:"column:ali_notify_url;type:varchar(500);not null;default:'';comment:支付宝异步通知地址" json:"ali_notify_url"`
|
||||
AliReturnURL string `gorm:"column:ali_return_url;type:varchar(500);not null;default:'';comment:支付宝同步跳转地址" json:"ali_return_url"`
|
||||
AliProduction bool `gorm:"column:ali_production;type:boolean;not null;default:false;comment:是否生产环境" json:"ali_production"`
|
||||
AliPayExpireMinutes int `gorm:"column:ali_pay_expire_minutes;type:integer;not null;default:30;comment:支付过期分钟数" json:"ali_pay_expire_minutes"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
|
||||
@@ -103,6 +103,18 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateWechatConfigRequest
|
||||
FyPublicKey: req.FyPublicKey,
|
||||
FyAPIURL: req.FyAPIURL,
|
||||
FyNotifyURL: req.FyNotifyURL,
|
||||
|
||||
AliAppID: req.AliAppID,
|
||||
AliPrivateKey: req.AliPrivateKey,
|
||||
AliPublicKey: req.AliPublicKey,
|
||||
AliNotifyURL: req.AliNotifyURL,
|
||||
AliReturnURL: req.AliReturnURL,
|
||||
AliProduction: req.AliProduction,
|
||||
AliPayExpireMinutes: req.AliPayExpireMinutes,
|
||||
}
|
||||
// 支付宝过期分钟数默认值
|
||||
if config.AliPayExpireMinutes == 0 {
|
||||
config.AliPayExpireMinutes = model.DefaultAliPayExpireMinutes
|
||||
}
|
||||
config.Creator = middleware.GetUserIDFromContext(ctx)
|
||||
|
||||
@@ -229,6 +241,19 @@ func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateWechatConf
|
||||
s.mergeStringField(&config.FyAPIURL, req.FyAPIURL)
|
||||
s.mergeStringField(&config.FyNotifyURL, req.FyNotifyURL)
|
||||
|
||||
// 支付宝支付
|
||||
s.mergeStringField(&config.AliAppID, req.AliAppID)
|
||||
s.mergeSensitiveField(&config.AliPrivateKey, req.AliPrivateKey)
|
||||
s.mergeSensitiveField(&config.AliPublicKey, req.AliPublicKey)
|
||||
s.mergeStringField(&config.AliNotifyURL, req.AliNotifyURL)
|
||||
s.mergeStringField(&config.AliReturnURL, req.AliReturnURL)
|
||||
if req.AliProduction != nil {
|
||||
config.AliProduction = *req.AliProduction
|
||||
}
|
||||
if req.AliPayExpireMinutes != nil && *req.AliPayExpireMinutes > 0 {
|
||||
config.AliPayExpireMinutes = *req.AliPayExpireMinutes
|
||||
}
|
||||
|
||||
config.Updater = middleware.GetUserIDFromContext(ctx)
|
||||
|
||||
if err := s.store.Update(ctx, config); err != nil {
|
||||
|
||||
@@ -150,3 +150,18 @@ func (s *PaymentStore) GetByOrderIDAndStatus(ctx context.Context, orderID uint,
|
||||
}
|
||||
return &payment, nil
|
||||
}
|
||||
|
||||
// FindLatestPendingByOrderAndMethod 按业务单、支付方式查找最新一条待支付记录
|
||||
// 用于支付宝支付单复用:同一业务单同一支付方式只允许一个有效 pending 支付单
|
||||
func (s *PaymentStore) FindLatestPendingByOrderAndMethod(ctx context.Context, orderID uint, orderType string, paymentMethod string) (*model.Payment, error) {
|
||||
var payment model.Payment
|
||||
err := s.db.WithContext(ctx).
|
||||
Where("order_id = ? AND order_type = ? AND payment_method = ? AND status = ?",
|
||||
orderID, orderType, paymentMethod, model.PaymentRecordStatusPending).
|
||||
Order("created_at DESC").
|
||||
First(&payment).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &payment, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user