fix(payment): restore upgrade-safe payment flows
This commit is contained in:
@@ -116,6 +116,17 @@ var providerSensitiveConfigFields = map[string]map[string]struct{}{
|
||||
payment.TypeStripe: {"secretkey": {}, "webhooksecret": {}},
|
||||
}
|
||||
|
||||
// providerPendingOrderProtectedConfigFields lists config keys that cannot be
|
||||
// changed while the instance has in-progress orders. This includes secrets plus
|
||||
// all provider identity fields that are snapshotted into orders or used by
|
||||
// webhook/refund verification.
|
||||
var providerPendingOrderProtectedConfigFields = map[string]map[string]struct{}{
|
||||
payment.TypeEasyPay: {"pkey": {}, "pid": {}},
|
||||
payment.TypeAlipay: {"privatekey": {}, "publickey": {}, "alipaypublickey": {}, "appid": {}},
|
||||
payment.TypeWxpay: {"privatekey": {}, "apiv3key": {}, "publickey": {}, "appid": {}, "mpappid": {}, "mchid": {}, "publickeyid": {}, "certserial": {}},
|
||||
payment.TypeStripe: {"secretkey": {}, "webhooksecret": {}},
|
||||
}
|
||||
|
||||
func isSensitiveProviderConfigField(providerKey, fieldName string) bool {
|
||||
fields, ok := providerSensitiveConfigFields[providerKey]
|
||||
if !ok {
|
||||
@@ -125,6 +136,28 @@ func isSensitiveProviderConfigField(providerKey, fieldName string) bool {
|
||||
return found
|
||||
}
|
||||
|
||||
func hasPendingOrderProtectedConfigChange(providerKey string, currentConfig, nextConfig map[string]string) bool {
|
||||
fields, ok := providerPendingOrderProtectedConfigFields[providerKey]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for fieldName := range fields {
|
||||
if providerConfigFieldValue(currentConfig, fieldName) != providerConfigFieldValue(nextConfig, fieldName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func providerConfigFieldValue(config map[string]string, fieldName string) string {
|
||||
for key, value := range config {
|
||||
if strings.EqualFold(key, fieldName) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *PaymentConfigService) countPendingOrders(ctx context.Context, providerInstanceID int64) (int, error) {
|
||||
return s.entClient.PaymentOrder.Query().
|
||||
Where(
|
||||
@@ -190,6 +223,18 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load provider instance: %w", err)
|
||||
}
|
||||
var pendingOrderCount *int
|
||||
getPendingOrderCount := func() (int, error) {
|
||||
if pendingOrderCount != nil {
|
||||
return *pendingOrderCount, nil
|
||||
}
|
||||
count, err := s.countPendingOrders(ctx, id)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("check pending orders: %w", err)
|
||||
}
|
||||
pendingOrderCount = &count
|
||||
return count, nil
|
||||
}
|
||||
nextEnabled := current.Enabled
|
||||
if req.Enabled != nil {
|
||||
nextEnabled = *req.Enabled
|
||||
@@ -201,18 +246,20 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in
|
||||
if err := s.validateVisibleMethodEnablementConflicts(ctx, id, current.ProviderKey, nextSupportedTypes, nextEnabled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mergedConfig map[string]string
|
||||
if req.Config != nil {
|
||||
hasSensitive := false
|
||||
for k, v := range req.Config {
|
||||
if v != "" && isSensitiveProviderConfigField(current.ProviderKey, k) {
|
||||
hasSensitive = true
|
||||
break
|
||||
}
|
||||
currentConfig, err := s.decryptConfig(current.Config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypt existing config: %w", err)
|
||||
}
|
||||
if hasSensitive {
|
||||
count, err := s.countPendingOrders(ctx, id)
|
||||
mergedConfig, err = s.mergeConfig(ctx, id, req.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasPendingOrderProtectedConfigChange(current.ProviderKey, currentConfig, mergedConfig) {
|
||||
count, err := getPendingOrderCount()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check pending orders: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil, infraerrors.Conflict("PENDING_ORDERS", "instance has pending orders").
|
||||
@@ -221,9 +268,9 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in
|
||||
}
|
||||
}
|
||||
if req.Enabled != nil && !*req.Enabled {
|
||||
count, err := s.countPendingOrders(ctx, id)
|
||||
count, err := getPendingOrderCount()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check pending orders: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil, infraerrors.Conflict("PENDING_ORDERS", "instance has pending orders").
|
||||
@@ -237,13 +284,6 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in
|
||||
if req.Enabled != nil {
|
||||
finalEnabled = *req.Enabled
|
||||
}
|
||||
var mergedConfig map[string]string
|
||||
if req.Config != nil {
|
||||
mergedConfig, err = s.mergeConfig(ctx, id, req.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if finalEnabled {
|
||||
configToValidate := mergedConfig
|
||||
if configToValidate == nil {
|
||||
@@ -269,9 +309,9 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in
|
||||
}
|
||||
if req.SupportedTypes != nil {
|
||||
// Check pending orders before removing payment types
|
||||
count, err := s.countPendingOrders(ctx, id)
|
||||
count, err := getPendingOrderCount()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check pending orders: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
// Load current instance to compare types
|
||||
|
||||
Reference in New Issue
Block a user