package payment import ( "context" "log" "gorm.io/gorm" "hfb_sys/backend/internal/model" "hfb_sys/backend/internal/modules/order" "hfb_sys/backend/internal/modules/paymentconfig" ) type Repository struct { db *gorm.DB configRepo *paymentconfig.Repository orderRepo *order.Repository } type runtimePaymentConfig struct { ID uint64 Provider string MerchantID string PayWay string JSPayFlag string NotifyURL string JumpURL string Channel channelClient } const ( channelSourceCreate = "create" channelSourceQuery = "query" channelSourceNotify = "notify" channelSourceMock = "mock" ) var refundBizTypes = []string{ "cancel_refund", "admin_close_refund", "admin_refund", "checkout_refund", "deposit_refund", "rent_refund", "arbitration_refund", } func RefundBizTypes() []string { out := make([]string, len(refundBizTypes)) copy(out, refundBizTypes) return out } // NewRepository 创建支付仓库,注入支付配置仓库和订单仓库。 func NewRepository(db *gorm.DB, configRepo *paymentconfig.Repository, orderRepo *order.Repository) *Repository { return &Repository{ db: db, configRepo: configRepo, orderRepo: orderRepo, } } // isMockMode 判断当前运行时配置是否为模拟支付。 func (c runtimePaymentConfig) isMockMode() bool { return c.Provider == "mock" } // defaultRuntimeConfig 按支付方式获取默认启用配置,用于新支付单创建。 func (r *Repository) defaultRuntimeConfig(ctx context.Context, payWay string) (*runtimePaymentConfig, error) { if r.configRepo == nil { return nil, ErrPaymentUnavailable } dto, err := r.configRepo.FindDefaultByPayWay(ctx, payWay, true) if err != nil { return nil, err } return runtimeConfigFromDTO(dto), nil } // runtimeConfigForPayment 回溯支付单当时命中的配置,优先使用 payment_config_id。 func (r *Repository) runtimeConfigForPayment(ctx context.Context, payment *model.PaymentOrder) (*runtimePaymentConfig, error) { provider := firstNonEmpty(payment.Provider, "mock") merchantID := payment.MerchantID payWay := normalizePayWay(payment.PayWay) if r.configRepo != nil && payment.PaymentConfigID > 0 { dto, err := r.configRepo.FindRuntimeByID(ctx, payment.PaymentConfigID, true) if err == nil { return runtimeConfigFromDTO(dto), nil } if err != paymentconfig.ErrConfigNotFound { return nil, err } } if r.configRepo != nil && merchantID != "" { dto, err := r.configRepo.FindByProviderMerchantPayWay(ctx, provider, merchantID, payWay, true) if err == nil { return runtimeConfigFromDTO(dto), nil } if err != paymentconfig.ErrConfigNotFound { return nil, err } dto, err = r.configRepo.FindByProviderMerchant(ctx, provider, merchantID, true) if err == nil { return runtimeConfigFromDTO(dto), nil } if err != paymentconfig.ErrConfigNotFound { return nil, err } } if provider != "leshua" { if provider == "lakala" && r.configRepo != nil { dto, err := r.configRepo.FindDefaultByProviderPayWay(ctx, provider, payWay, true) if err != nil { return nil, err } return runtimeConfigFromDTO(dto), nil } return &runtimePaymentConfig{ Provider: provider, MerchantID: merchantID, }, nil } if r.configRepo == nil { return nil, ErrPaymentUnavailable } dto, err := r.configRepo.FindDefaultByProviderPayWay(ctx, provider, payWay, true) if err != nil { return nil, err } return runtimeConfigFromDTO(dto), nil } // runtimeConfigFromDTO 将后台支付配置 DTO 转为支付运行时配置。 func runtimeConfigFromDTO(dto *paymentconfig.ConfigDTO) *runtimePaymentConfig { provider := firstNonEmpty(dto.Provider, "mock") payWay := normalizePayWay(dto.PayWay) jsPayFlag := firstNonEmpty(dto.JSPayFlag, "2") client, err := buildChannelClient(dto) if err != nil { client = nil } return &runtimePaymentConfig{ ID: dto.ID, Provider: provider, MerchantID: dto.MerchantID, PayWay: payWay, JSPayFlag: jsPayFlag, NotifyURL: dto.NotifyURL, JumpURL: dto.JumpURL, Channel: client, } } // recordConfigUsage 记录成功支付使用的配置,失败只写日志不影响主流程。 func (r *Repository) recordConfigUsage(ctx context.Context, runtimeConfig *runtimePaymentConfig, payment *model.PaymentOrder) { if r.configRepo == nil || runtimeConfig == nil || payment == nil || runtimeConfig.ID == 0 { return } if payment.BizType != "order_pay" || payment.Status != "paid" { return } if err := r.configRepo.RecordUsage(ctx, runtimeConfig.ID, payment.ID, runtimeConfig.Provider, runtimeConfig.MerchantID, payment.AmountCent, payment.BizType); err != nil { log.Printf("[payment] record config usage failed config_id=%d payment_id=%d err=%v", runtimeConfig.ID, payment.ID, err) } }