新增收款商户、商户池轮询、微信授权配置独立管理;三类新支付 (C端套餐购买、C端资产钱包充值、代理在线预存款充值)无条件 经商户池选择并冻结路由,无旧综合配置回退。merchant_id 为空 历史支付继续按 payment_config_id 双读。凭证版本化加载与 ID+版本缓存保证轮换一致性。删除商户池新支付创建开关及全部 引用。
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/application/merchantpayment"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
@@ -33,9 +34,10 @@ const (
|
||||
clientPurchaseLockTTL = 10 * time.Second
|
||||
)
|
||||
|
||||
// WechatConfigServiceInterface 微信配置服务接口。
|
||||
// WechatConfigServiceInterface 只读取历史支付单的综合支付配置。
|
||||
type WechatConfigServiceInterface interface {
|
||||
GetActiveConfig(ctx context.Context) (*model.WechatConfig, error)
|
||||
GetAuthorizationConfig(ctx context.Context) (*model.WechatConfig, error)
|
||||
GetByIDUnscoped(ctx context.Context, id uint) (*model.WechatConfig, error)
|
||||
}
|
||||
|
||||
// OrderWalletPayServiceInterface 订单钱包支付服务接口。
|
||||
@@ -70,7 +72,6 @@ type Service struct {
|
||||
openIDStore *postgres.PersonalCustomerOpenIDStore
|
||||
personalCustomerStore *postgres.PersonalCustomerStore
|
||||
personalCustomerPhoneStore *postgres.PersonalCustomerPhoneStore
|
||||
wechatConfigService WechatConfigServiceInterface
|
||||
orderPaymentService OrderWalletPayServiceInterface
|
||||
packageSeriesStore *postgres.PackageSeriesStore
|
||||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore
|
||||
@@ -82,6 +83,8 @@ type Service struct {
|
||||
paymentMethodPolicy PaymentMethodPolicy
|
||||
auditWriter *audit.Writer
|
||||
paymentIntegration *integrationlog.Repository
|
||||
merchantRuntime *merchantpayment.RuntimeLoader
|
||||
wechatConfigService WechatConfigServiceInterface
|
||||
}
|
||||
|
||||
// SetPaymentMethodPolicy 注入 C 端支付方式策略。
|
||||
@@ -95,6 +98,11 @@ func (s *Service) SetPaymentAudit(writer *audit.Writer, integration *integration
|
||||
s.paymentIntegration = integration
|
||||
}
|
||||
|
||||
// SetLegacyPaymentConfigService 注入仅供 merchant_id 为空历史支付单使用的旧配置读取。
|
||||
func (s *Service) SetLegacyPaymentConfigService(wechatConfigService WechatConfigServiceInterface) {
|
||||
s.wechatConfigService = wechatConfigService
|
||||
}
|
||||
|
||||
// New 创建客户端订单服务。
|
||||
func New(
|
||||
assetService *asset.Service,
|
||||
@@ -107,7 +115,6 @@ func New(
|
||||
openIDStore *postgres.PersonalCustomerOpenIDStore,
|
||||
personalCustomerStore *postgres.PersonalCustomerStore,
|
||||
personalCustomerPhoneStore *postgres.PersonalCustomerPhoneStore,
|
||||
wechatConfigService WechatConfigServiceInterface,
|
||||
orderPaymentService OrderWalletPayServiceInterface,
|
||||
packageSeriesStore *postgres.PackageSeriesStore,
|
||||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore,
|
||||
@@ -128,7 +135,6 @@ func New(
|
||||
openIDStore: openIDStore,
|
||||
personalCustomerStore: personalCustomerStore,
|
||||
personalCustomerPhoneStore: personalCustomerPhoneStore,
|
||||
wechatConfigService: wechatConfigService,
|
||||
orderPaymentService: orderPaymentService,
|
||||
packageSeriesStore: packageSeriesStore,
|
||||
shopSeriesAllocationStore: shopSeriesAllocationStore,
|
||||
@@ -137,6 +143,7 @@ func New(
|
||||
db: db,
|
||||
redis: redisClient,
|
||||
logger: logger,
|
||||
merchantRuntime: merchantpayment.NewRuntimeLoader(db, redisClient),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,18 +308,10 @@ func (s *Service) CreateOrder(ctx context.Context, customerID uint, req *dto.Cli
|
||||
// }
|
||||
|
||||
if req.PaymentMethod == model.PaymentMethodAlipay {
|
||||
// 支付宝强充:不需要 app_type / OpenID
|
||||
activeConfig, err := s.wechatConfigService.GetActiveConfig(skipCtx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询支付配置失败")
|
||||
}
|
||||
if activeConfig == nil {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "未找到生效的支付配置")
|
||||
}
|
||||
return s.createAlipayForceRechargeOrder(skipCtx, customerID, assetInfo, validationResult, activeConfig, forceRecharge, redisKey, &created)
|
||||
return s.createAlipayForceRechargeOrder(skipCtx, customerID, assetInfo, validationResult, forceRecharge, redisKey, &created)
|
||||
}
|
||||
// 微信强充:app_type 必须传入
|
||||
activeConfig, appID, err := s.resolveWechatConfig(skipCtx, req.AppType)
|
||||
authorization, appID, err := s.loadWechatAuthorization(skipCtx, req.AppType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -320,11 +319,7 @@ func (s *Service) CreateOrder(ctx context.Context, customerID uint, req *dto.Cli
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paymentProvider, err := s.newPaymentProvider(activeConfig, appID, req.AppType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.createForceRechargeOrder(skipCtx, customerID, openID, assetInfo, validationResult, activeConfig, forceRecharge, redisKey, paymentProvider, &created)
|
||||
return s.createForceRechargeOrder(skipCtx, customerID, openID, assetInfo, validationResult, authorization, appID, req.AppType, forceRecharge, redisKey, &created)
|
||||
}
|
||||
|
||||
return s.createPackageOrder(skipCtx, customerID, validationResult, req.PaymentMethod, redisKey, &created)
|
||||
@@ -362,31 +357,49 @@ func (s *Service) validatePurchase(ctx context.Context, assetInfo *dto.AssetReso
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) resolveWechatConfig(ctx context.Context, appType string) (*model.WechatConfig, string, error) {
|
||||
activeConfig, err := s.wechatConfigService.GetActiveConfig(ctx)
|
||||
func (s *Service) loadWechatAuthorization(ctx context.Context, appType string) (*model.WechatAuthorization, string, error) {
|
||||
if s.merchantRuntime == nil {
|
||||
return nil, "", errors.New(errors.CodeServiceUnavailable, "商户池路由能力未配置")
|
||||
}
|
||||
authorization, err := s.merchantRuntime.LoadAuthorization(ctx)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(errors.CodeDatabaseError, err, "查询微信配置失败")
|
||||
return nil, "", err
|
||||
}
|
||||
if activeConfig == nil {
|
||||
return nil, "", errors.New(errors.CodeWechatPayFailed, "未找到生效的微信支付配置")
|
||||
}
|
||||
|
||||
switch appType {
|
||||
case "official_account":
|
||||
if activeConfig.OaAppID == "" {
|
||||
return nil, "", errors.New(errors.CodeWechatPayFailed, "公众号支付配置不完整")
|
||||
if strings.TrimSpace(authorization.OaAppID) == "" {
|
||||
return nil, "", errors.New(errors.CodeWechatConfigUnavailable, "微信授权未配置")
|
||||
}
|
||||
return activeConfig, activeConfig.OaAppID, nil
|
||||
return authorization, authorization.OaAppID, nil
|
||||
case "miniapp":
|
||||
if activeConfig.MiniappAppID == "" {
|
||||
return nil, "", errors.New(errors.CodeWechatPayFailed, "小程序支付配置不完整")
|
||||
if strings.TrimSpace(authorization.MiniappAppID) == "" {
|
||||
return nil, "", errors.New(errors.CodeWechatConfigUnavailable, "微信授权未配置")
|
||||
}
|
||||
return activeConfig, activeConfig.MiniappAppID, nil
|
||||
return authorization, authorization.MiniappAppID, nil
|
||||
default:
|
||||
return nil, "", errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) selectMerchantConfig(ctx context.Context, tx *gorm.DB, paymentMethod string, authorization *model.WechatAuthorization) (*merchantpayment.RouteSelection, *model.WechatConfig, error) {
|
||||
if s.merchantRuntime == nil {
|
||||
return nil, nil, errors.New(errors.CodeServiceUnavailable, "商户池路由能力未配置")
|
||||
}
|
||||
route, err := s.merchantRuntime.SelectForNewPaymentWithTx(ctx, tx, paymentMethod, time.Now())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
config, err := merchantpayment.MerchantConfig(route.Merchant, authorization)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return route, config, nil
|
||||
}
|
||||
|
||||
func freezePaymentRoute(payment *model.Payment, route *merchantpayment.RouteSelection) {
|
||||
merchantpayment.FreezeRoute(payment, route)
|
||||
}
|
||||
|
||||
func (s *Service) resolveCustomerOpenID(ctx context.Context, customerID uint, appID string) (string, error) {
|
||||
records, err := s.openIDStore.ListByCustomerID(ctx, customerID)
|
||||
if err != nil {
|
||||
@@ -405,6 +418,41 @@ func (s *Service) resolveCustomerOpenID(ctx context.Context, customerID uint, ap
|
||||
return "", errors.New(errors.CodeNotFound, "未找到当前应用的微信授权信息")
|
||||
}
|
||||
|
||||
// merchantConfigForPayment 按冻结商户或历史 payment_config_id 加载支付凭证。
|
||||
// merchant_id 为空仅是历史支付保留期兼容:独立 Change 清理后删除此读取,
|
||||
// 绝不按当前商户池或综合配置推断、回填该支付的商户。
|
||||
func (s *Service) merchantConfigForPayment(ctx context.Context, payment *model.Payment) (*model.WechatConfig, error) {
|
||||
if payment != nil && payment.MerchantID != nil {
|
||||
if s.merchantRuntime == nil {
|
||||
return nil, errors.New(errors.CodeServiceUnavailable, "商户池路由能力未配置")
|
||||
}
|
||||
merchant, err := s.merchantRuntime.LoadMerchant(ctx, *payment.MerchantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 仅微信直连(v3/v2)商户需要全局授权配置中的 AppID;富友商户不依赖该配置。
|
||||
var authorization *model.WechatAuthorization
|
||||
if merchant.ProviderType == model.ProviderTypeWechat || merchant.ProviderType == model.ProviderTypeWechatV2 {
|
||||
authorization, err = s.merchantRuntime.LoadAuthorization(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return merchantpayment.MerchantConfig(merchant, authorization)
|
||||
}
|
||||
if payment == nil || payment.PaymentConfigID == nil {
|
||||
return nil, errors.New(errors.CodeNoPaymentConfig, "历史支付宝支付单缺少支付配置")
|
||||
}
|
||||
if s.wechatConfigService == nil {
|
||||
return nil, errors.New(errors.CodeServiceUnavailable, "旧支付配置加载能力未配置")
|
||||
}
|
||||
config, err := s.wechatConfigService.GetByIDUnscoped(ctx, *payment.PaymentConfigID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeNoPaymentConfig, err, "历史支付宝支付配置不可用")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (s *Service) createPackageOrder(
|
||||
ctx context.Context,
|
||||
customerID uint,
|
||||
@@ -465,10 +513,10 @@ func (s *Service) createForceRechargeOrder(
|
||||
openID string,
|
||||
assetInfo *dto.AssetResolveResponse,
|
||||
validationResult *purchase_validation.PurchaseValidationResult,
|
||||
activeConfig *model.WechatConfig,
|
||||
authorization *model.WechatAuthorization,
|
||||
appID, appType string,
|
||||
forceRecharge *ForceRechargeRequirement,
|
||||
redisKey string,
|
||||
paymentProvider PaymentProvider,
|
||||
created *bool,
|
||||
) (*dto.ClientCreateOrderResponse, error) {
|
||||
resourceType, resourceID, err := resolveWalletResource(validationResult)
|
||||
@@ -519,16 +567,27 @@ func (s *Service) createForceRechargeOrder(
|
||||
|
||||
paymentNo := generateClientPaymentNo()
|
||||
payment := &model.Payment{
|
||||
PaymentNo: paymentNo,
|
||||
OrderID: rechargeOrder.ID,
|
||||
OrderType: model.PaymentOrderTypeRecharge,
|
||||
PaymentMethod: model.PaymentByWechat,
|
||||
Amount: forceRecharge.ForceRechargeAmount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
PaymentConfigID: &activeConfig.ID,
|
||||
PaymentNo: paymentNo,
|
||||
OrderID: rechargeOrder.ID,
|
||||
OrderType: model.PaymentOrderTypeRecharge,
|
||||
PaymentMethod: model.PaymentByWechat,
|
||||
Amount: forceRecharge.ForceRechargeAmount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
}
|
||||
var activeConfig *model.WechatConfig
|
||||
var paymentProvider PaymentProvider
|
||||
|
||||
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
route, config, err := s.selectMerchantConfig(ctx, tx, model.PaymentByWechat, authorization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
paymentProvider, err = s.newPaymentProvider(config, appID, appType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
activeConfig = config
|
||||
freezePaymentRoute(payment, route)
|
||||
if err := s.rechargeOrderStore.CreateWithTx(ctx, tx, rechargeOrder); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建充值订单失败")
|
||||
}
|
||||
@@ -967,26 +1026,22 @@ func (s *Service) getOrBuildAlipayPaymentLink(
|
||||
amount int64,
|
||||
subject string,
|
||||
) (*dto.ClientPaymentLink, error) {
|
||||
activeConfig, err := s.wechatConfigService.GetActiveConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询支付配置失败")
|
||||
}
|
||||
if activeConfig == nil {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "未找到生效的支付配置")
|
||||
}
|
||||
|
||||
// 查找最新的 alipay 待支付单
|
||||
existing, _ := s.paymentStore.FindLatestPendingByOrderAndMethod(
|
||||
ctx, orderID, orderType, model.PaymentByAlipay,
|
||||
)
|
||||
|
||||
var payment *model.Payment
|
||||
var activeConfig *model.WechatConfig
|
||||
created := false
|
||||
now := time.Now()
|
||||
if existing != nil && existing.ExpireAt != nil && existing.ExpireAt.After(now) {
|
||||
// 复用未过期的支付单
|
||||
payment = existing
|
||||
var err error
|
||||
activeConfig, err = s.merchantConfigForPayment(ctx, payment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// 过期或不存在,标记旧单 failed 并新建
|
||||
if existing != nil {
|
||||
if updateErr := s.markPaymentFailed(ctx, existing, "支付宝支付记录过期关闭"); updateErr != nil {
|
||||
s.logger.Warn("标记过期支付宝支付单 failed 失败",
|
||||
@@ -995,44 +1050,51 @@ func (s *Service) getOrBuildAlipayPaymentLink(
|
||||
)
|
||||
}
|
||||
}
|
||||
expireMinutes := activeConfig.AliPayExpireMinutes
|
||||
if expireMinutes <= 0 {
|
||||
expireMinutes = 30
|
||||
}
|
||||
expireAt := time.Now().Add(time.Duration(expireMinutes) * time.Minute)
|
||||
newPayment := &model.Payment{
|
||||
PaymentNo: generateClientPaymentNo(),
|
||||
OrderID: orderID,
|
||||
OrderType: orderType,
|
||||
PaymentMethod: model.PaymentByAlipay,
|
||||
Amount: amount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
PaymentConfigID: &activeConfig.ID,
|
||||
ExpireAt: &expireAt,
|
||||
}
|
||||
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
route, config, err := s.selectMerchantConfig(ctx, tx, model.PaymentByAlipay, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expireMinutes := config.AliPayExpireMinutes
|
||||
if expireMinutes <= 0 {
|
||||
expireMinutes = 30
|
||||
}
|
||||
expireAt := time.Now().Add(time.Duration(expireMinutes) * time.Minute)
|
||||
newPayment := &model.Payment{
|
||||
PaymentNo: generateClientPaymentNo(),
|
||||
OrderID: orderID,
|
||||
OrderType: orderType,
|
||||
PaymentMethod: model.PaymentByAlipay,
|
||||
Amount: amount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
ExpireAt: &expireAt,
|
||||
}
|
||||
freezePaymentRoute(newPayment, route)
|
||||
if err := s.paymentStore.CreateWithTx(ctx, tx, newPayment); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建支付宝支付单失败")
|
||||
}
|
||||
return s.appendPaymentCreatedAudit(ctx, tx, newPayment, nil, nil)
|
||||
if err := s.appendPaymentCreatedAudit(ctx, tx, newPayment, nil, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
payment = newPayment
|
||||
activeConfig = config
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payment = newPayment
|
||||
created = true
|
||||
s.logger.Info("创建支付宝支付单",
|
||||
zap.String("payment_no", payment.PaymentNo),
|
||||
zap.String("order_type", orderType),
|
||||
zap.Uint("order_id", orderID),
|
||||
zap.Int64("amount", amount),
|
||||
zap.Time("expire_at", expireAt),
|
||||
zap.Uint("config_id", activeConfig.ID),
|
||||
zap.Time("expire_at", *payment.ExpireAt),
|
||||
)
|
||||
}
|
||||
|
||||
wapURL, err := alipay.BuildWapPayURL(ctx, activeConfig, payment, subject)
|
||||
if err != nil {
|
||||
// 新建的 payment 生成链接失败,标记 failed
|
||||
if existing == nil || payment.ID != existing.ID {
|
||||
if created {
|
||||
_ = s.markPaymentFailed(ctx, payment, "支付宝支付链接生成失败,关闭支付记录")
|
||||
}
|
||||
return nil, err
|
||||
@@ -1056,7 +1118,6 @@ func (s *Service) createAlipayForceRechargeOrder(
|
||||
customerID uint,
|
||||
assetInfo *dto.AssetResolveResponse,
|
||||
validationResult *purchase_validation.PurchaseValidationResult,
|
||||
activeConfig *model.WechatConfig,
|
||||
forceRecharge *ForceRechargeRequirement,
|
||||
redisKey string,
|
||||
created *bool,
|
||||
@@ -1105,27 +1166,31 @@ func (s *Service) createAlipayForceRechargeOrder(
|
||||
LinkedCarrierType: assetInfo.AssetType,
|
||||
LinkedCarrierID: &resourceID,
|
||||
AutoPurchaseStatus: model.AutoPurchaseStatusPending,
|
||||
PaymentConfigID: &activeConfig.ID,
|
||||
}
|
||||
|
||||
expireMinutesForce := activeConfig.AliPayExpireMinutes
|
||||
if expireMinutesForce <= 0 {
|
||||
expireMinutesForce = 30
|
||||
}
|
||||
expireAt := time.Now().Add(time.Duration(expireMinutesForce) * time.Minute)
|
||||
paymentNo := generateClientPaymentNo()
|
||||
payment := &model.Payment{
|
||||
PaymentNo: paymentNo,
|
||||
OrderType: model.PaymentOrderTypeRecharge,
|
||||
PaymentMethod: model.PaymentByAlipay,
|
||||
Amount: forceRecharge.ForceRechargeAmount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
PaymentConfigID: &activeConfig.ID,
|
||||
ExpireAt: &expireAt,
|
||||
PaymentNo: paymentNo,
|
||||
OrderType: model.PaymentOrderTypeRecharge,
|
||||
PaymentMethod: model.PaymentByAlipay,
|
||||
Amount: forceRecharge.ForceRechargeAmount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
}
|
||||
|
||||
// 事务创建充值单 + 支付单(WAP URL 本地签名,不需要先调第三方)
|
||||
var activeConfig *model.WechatConfig
|
||||
var expireAt time.Time
|
||||
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
route, config, err := s.selectMerchantConfig(ctx, tx, model.PaymentByAlipay, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expireMinutes := config.AliPayExpireMinutes
|
||||
if expireMinutes <= 0 {
|
||||
expireMinutes = 30
|
||||
}
|
||||
expireAt = time.Now().Add(time.Duration(expireMinutes) * time.Minute)
|
||||
activeConfig = config
|
||||
payment.ExpireAt = &expireAt
|
||||
freezePaymentRoute(payment, route)
|
||||
if err := s.rechargeOrderStore.CreateWithTx(ctx, tx, rechargeOrder); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建充值订单失败")
|
||||
}
|
||||
@@ -1154,7 +1219,6 @@ func (s *Service) createAlipayForceRechargeOrder(
|
||||
zap.String("recharge_no", rechargeOrderNo),
|
||||
zap.Int64("amount", forceRecharge.ForceRechargeAmount),
|
||||
zap.Time("expire_at", expireAt),
|
||||
zap.Uint("config_id", activeConfig.ID),
|
||||
)
|
||||
|
||||
s.markClientPurchaseCreated(ctx, redisKey, rechargeOrderNo)
|
||||
@@ -1432,7 +1496,7 @@ func (s *Service) PayOrder(ctx context.Context, customerID uint, orderID uint, r
|
||||
return &dto.ClientPayOrderResponse{PaymentMethod: paymentMethod}, nil
|
||||
|
||||
case model.PaymentMethodWechat:
|
||||
activeConfig, appID, err := s.resolveWechatConfig(skipCtx, req.AppType)
|
||||
authorization, appID, err := s.loadWechatAuthorization(skipCtx, req.AppType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1440,29 +1504,29 @@ func (s *Service) PayOrder(ctx context.Context, customerID uint, orderID uint, r
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paymentProvider, err := s.newPaymentProvider(activeConfig, appID, req.AppType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paymentNo := generateClientPaymentNo()
|
||||
payment := &model.Payment{
|
||||
PaymentNo: paymentNo,
|
||||
OrderID: order.ID,
|
||||
OrderType: model.PaymentOrderTypePackage,
|
||||
PaymentMethod: model.PaymentByWechat,
|
||||
Amount: order.TotalAmount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
PaymentConfigID: &activeConfig.ID,
|
||||
PaymentNo: paymentNo,
|
||||
OrderID: order.ID,
|
||||
OrderType: model.PaymentOrderTypePackage,
|
||||
PaymentMethod: model.PaymentByWechat,
|
||||
Amount: order.TotalAmount,
|
||||
Status: model.PaymentRecordStatusPending,
|
||||
}
|
||||
|
||||
var activeConfig *model.WechatConfig
|
||||
var paymentProvider PaymentProvider
|
||||
if err := s.db.WithContext(skipCtx).Transaction(func(tx *gorm.DB) error {
|
||||
// 订单仍保留最近一次支付配置,兼容旧 ORD 回调。
|
||||
if err := tx.Model(&model.Order{}).
|
||||
Where("id = ?", orderID).
|
||||
Update("payment_config_id", activeConfig.ID).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新订单支付配置失败")
|
||||
route, config, err := s.selectMerchantConfig(skipCtx, tx, model.PaymentByWechat, authorization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
paymentProvider, err = s.newPaymentProvider(config, appID, req.AppType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
activeConfig = config
|
||||
freezePaymentRoute(payment, route)
|
||||
if err := s.paymentStore.CreateWithTx(skipCtx, tx, payment); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建支付记录失败")
|
||||
}
|
||||
@@ -1498,30 +1562,12 @@ func (s *Service) PayOrder(ctx context.Context, customerID uint, orderID uint, r
|
||||
}, nil
|
||||
|
||||
case model.PaymentMethodAlipay:
|
||||
// 支付宝支付:不需要 app_type / OpenID
|
||||
activeConfig, err := s.wechatConfigService.GetActiveConfig(skipCtx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询支付配置失败")
|
||||
}
|
||||
if activeConfig == nil {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "未找到生效的支付配置")
|
||||
}
|
||||
|
||||
paymentLink, err := s.getOrBuildAlipayPaymentLink(
|
||||
skipCtx, orderID, model.PaymentOrderTypePackage, order.TotalAmount, "套餐购买",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 更新订单支付配置 ID,兼容回调配置回溯
|
||||
if dbErr := s.db.WithContext(skipCtx).Model(&model.Order{}).
|
||||
Where("id = ?", orderID).
|
||||
Update("payment_config_id", activeConfig.ID).Error; dbErr != nil {
|
||||
s.logger.Warn("更新订单支付配置 ID 失败",
|
||||
zap.Uint("order_id", orderID),
|
||||
zap.Error(dbErr),
|
||||
)
|
||||
}
|
||||
return &dto.ClientPayOrderResponse{
|
||||
PaymentMethod: paymentMethod,
|
||||
PaymentLink: paymentLink,
|
||||
|
||||
Reference in New Issue
Block a user