fix(upgrade): preserve legacy auth and payment compatibility
This commit is contained in:
@@ -45,10 +45,18 @@ func (s *PaymentConfigService) pcApplyEnabledVisibleMethodInstances(ctx context.
|
||||
for _, method := range []string{payment.TypeAlipay, payment.TypeWxpay} {
|
||||
matching := filterEnabledVisibleMethodInstances(instances, method)
|
||||
providerKey, err := s.resolveVisibleMethodProviderKey(ctx, method, matching)
|
||||
if err != nil || providerKey == "" {
|
||||
if err != nil {
|
||||
delete(filtered, method)
|
||||
continue
|
||||
}
|
||||
if providerKey == "" {
|
||||
if len(matching) == 0 {
|
||||
delete(filtered, method)
|
||||
continue
|
||||
}
|
||||
filtered[method] = matching
|
||||
continue
|
||||
}
|
||||
selectedInstances := filterVisibleMethodInstancesByProviderKey(instances, method, providerKey)
|
||||
if len(selectedInstances) == 0 {
|
||||
delete(filtered, method)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||||
"github.com/Wei-Shaw/sub2api/internal/payment"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnionFloat(t *testing.T) {
|
||||
@@ -402,3 +403,59 @@ func TestGetAvailableMethodLimitsUsesConfiguredVisibleMethodSource(t *testing.T)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAvailableMethodLimitsPreservesLegacyCrossProviderBehaviorWhenVisibleMethodSourceMissing(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := newPaymentConfigServiceTestClient(t)
|
||||
|
||||
_, err := client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeAlipay).
|
||||
SetName("Official Alipay").
|
||||
SetConfig("{}").
|
||||
SetSupportedTypes("alipay").
|
||||
SetLimits(`{"alipay":{"singleMin":10,"singleMax":100}}`).
|
||||
SetEnabled(true).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeEasyPay).
|
||||
SetName("EasyPay Mixed").
|
||||
SetConfig("{}").
|
||||
SetSupportedTypes("alipay,wxpay").
|
||||
SetLimits(`{"alipay":{"singleMin":20,"singleMax":200},"wxpay":{"singleMin":40,"singleMax":400}}`).
|
||||
SetEnabled(true).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeWxpay).
|
||||
SetName("Official WeChat").
|
||||
SetConfig("{}").
|
||||
SetSupportedTypes("wxpay").
|
||||
SetLimits(`{"wxpay":{"singleMin":30,"singleMax":300}}`).
|
||||
SetEnabled(true).
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
svc := &PaymentConfigService{
|
||||
entClient: client,
|
||||
settingRepo: &paymentConfigSettingRepoStub{values: map[string]string{}},
|
||||
}
|
||||
|
||||
resp, err := svc.GetAvailableMethodLimits(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
alipayLimits, ok := resp.Methods[payment.TypeAlipay]
|
||||
require.True(t, ok, "expected alipay limits to remain visible")
|
||||
require.Equal(t, 10.0, alipayLimits.SingleMin)
|
||||
require.Equal(t, 200.0, alipayLimits.SingleMax)
|
||||
|
||||
wxpayLimits, ok := resp.Methods[payment.TypeWxpay]
|
||||
require.True(t, ok, "expected wxpay limits to remain visible")
|
||||
require.Equal(t, 30.0, wxpayLimits.SingleMin)
|
||||
require.Equal(t, 400.0, wxpayLimits.SingleMax)
|
||||
|
||||
require.Equal(t, 10.0, resp.GlobalMin)
|
||||
require.Equal(t, 400.0, resp.GlobalMax)
|
||||
}
|
||||
|
||||
@@ -586,7 +586,60 @@ func TestVisibleMethodLoadBalancerUsesConfiguredSourceWhenMultipleProvidersEnabl
|
||||
}
|
||||
}
|
||||
|
||||
func TestVisibleMethodLoadBalancerRejectsMissingOrInvalidSourceWhenMultipleProvidersEnabled(t *testing.T) {
|
||||
func TestVisibleMethodLoadBalancerPreservesLegacyCrossProviderRoutingWhenSourceMissing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
client := newPaymentConfigServiceTestClient(t)
|
||||
|
||||
_, err := client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeAlipay).
|
||||
SetName("Official Alipay").
|
||||
SetConfig("{}").
|
||||
SetSupportedTypes("alipay").
|
||||
SetEnabled(true).
|
||||
SetSortOrder(1).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("create official provider: %v", err)
|
||||
}
|
||||
|
||||
_, err = client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeEasyPay).
|
||||
SetName("EasyPay Alipay").
|
||||
SetConfig("{}").
|
||||
SetSupportedTypes("alipay").
|
||||
SetEnabled(true).
|
||||
SetSortOrder(2).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("create easypay provider: %v", err)
|
||||
}
|
||||
|
||||
inner := &captureLoadBalancer{}
|
||||
configService := &PaymentConfigService{
|
||||
entClient: client,
|
||||
settingRepo: &paymentConfigSettingRepoStub{
|
||||
values: map[string]string{
|
||||
visibleMethodSourceSettingKey(payment.TypeAlipay): "",
|
||||
},
|
||||
},
|
||||
}
|
||||
lb := newVisibleMethodLoadBalancer(inner, configService)
|
||||
|
||||
_, err = lb.SelectInstance(ctx, "", payment.TypeAlipay, payment.StrategyRoundRobin, 9.9)
|
||||
if err != nil {
|
||||
t.Fatalf("SelectInstance returned error: %v", err)
|
||||
}
|
||||
if inner.lastProviderKey != "" {
|
||||
t.Fatalf("lastProviderKey = %q, want legacy cross-provider empty key", inner.lastProviderKey)
|
||||
}
|
||||
if inner.lastPaymentType != payment.TypeAlipay {
|
||||
t.Fatalf("lastPaymentType = %q, want %q", inner.lastPaymentType, payment.TypeAlipay)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVisibleMethodLoadBalancerRejectsInvalidSourceWhenMultipleProvidersEnabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
@@ -595,12 +648,6 @@ func TestVisibleMethodLoadBalancerRejectsMissingOrInvalidSourceWhenMultipleProvi
|
||||
sourceValue string
|
||||
wantMessage string
|
||||
}{
|
||||
{
|
||||
name: "missing alipay source",
|
||||
method: payment.TypeAlipay,
|
||||
sourceValue: "",
|
||||
wantMessage: "alipay source is required when the visible method is enabled",
|
||||
},
|
||||
{
|
||||
name: "invalid wxpay source",
|
||||
method: payment.TypeWxpay,
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -166,15 +167,21 @@ func (s *PaymentConfigService) resolveVisibleMethodSourceProviderKey(ctx context
|
||||
if s != nil && s.settingRepo != nil && sourceKey != "" {
|
||||
value, err := s.settingRepo.GetValue(ctx, sourceKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get %s: %w", sourceKey, err)
|
||||
if !errors.Is(err, ErrSettingNotFound) {
|
||||
return "", fmt.Errorf("get %s: %w", sourceKey, err)
|
||||
}
|
||||
} else {
|
||||
rawSource = value
|
||||
}
|
||||
rawSource = value
|
||||
}
|
||||
|
||||
normalizedSource, err := normalizeVisibleMethodSettingSource(method, rawSource, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if normalizedSource == "" {
|
||||
return "", nil
|
||||
}
|
||||
providerKey, ok := VisibleMethodProviderKeyForSource(method, normalizedSource)
|
||||
if !ok {
|
||||
return "", infraerrors.BadRequest(
|
||||
@@ -200,6 +207,9 @@ func (s *PaymentConfigService) resolveVisibleMethodProviderKey(
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if providerKey == "" {
|
||||
return "", nil
|
||||
}
|
||||
selected := selectVisibleMethodInstanceByProviderKey(matching, providerKey)
|
||||
if selected == nil {
|
||||
return "", infraerrors.BadRequest(
|
||||
@@ -237,5 +247,11 @@ func (s *PaymentConfigService) resolveEnabledVisibleMethodInstance(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if providerKey == "" {
|
||||
if len(matching) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &dbent.PaymentProviderInstance{ProviderKey: ""}, nil
|
||||
}
|
||||
return selectVisibleMethodInstanceByProviderKey(matching, providerKey), nil
|
||||
}
|
||||
|
||||
@@ -282,7 +282,19 @@ func mergeWeChatConnectCapabilitySettings(settings map[string]string, base confi
|
||||
mobileConfigured := hasMobile && strings.TrimSpace(rawMobile) != ""
|
||||
|
||||
if openConfigured || mpConfigured || mobileConfigured {
|
||||
return parseWeChatConnectCapabilitySettings(settings, enabled, mode)
|
||||
openEnabled := strings.TrimSpace(rawOpen) == "true"
|
||||
mpEnabled := strings.TrimSpace(rawMP) == "true"
|
||||
mobileEnabled := strings.TrimSpace(rawMobile) == "true"
|
||||
_, enabledConfigured := settings[SettingKeyWeChatConnectEnabled]
|
||||
if !enabledConfigured &&
|
||||
enabled &&
|
||||
!openEnabled &&
|
||||
!mpEnabled &&
|
||||
!mobileEnabled &&
|
||||
(base.OpenEnabled || base.MPEnabled || base.MobileEnabled) {
|
||||
return base.OpenEnabled, base.MPEnabled, base.MobileEnabled
|
||||
}
|
||||
return openEnabled, mpEnabled, mobileEnabled
|
||||
}
|
||||
if !enabled {
|
||||
return false, false, false
|
||||
@@ -1921,14 +1933,9 @@ func isFalseSettingValue(value string) bool {
|
||||
}
|
||||
|
||||
func normalizeVisibleMethodSettingSource(method, source string, enabled bool) (string, error) {
|
||||
_ = enabled
|
||||
source = strings.TrimSpace(source)
|
||||
if source == "" {
|
||||
if enabled {
|
||||
return "", infraerrors.BadRequest(
|
||||
"INVALID_PAYMENT_VISIBLE_METHOD_SOURCE",
|
||||
fmt.Sprintf("%s source is required when the visible method is enabled", method),
|
||||
)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,36 @@ func TestSettingService_GetWeChatConnectOAuthConfig_FallsBackToConfigWhenDatabas
|
||||
require.Empty(t, got.RedirectURL)
|
||||
}
|
||||
|
||||
func TestSettingService_GetWeChatConnectOAuthConfig_IgnoresSyntheticDisabledCapabilitiesFromMigration118(t *testing.T) {
|
||||
repo := &settingWeChatRepoStub{
|
||||
values: map[string]string{
|
||||
SettingKeyWeChatConnectOpenEnabled: "false",
|
||||
SettingKeyWeChatConnectMPEnabled: "false",
|
||||
},
|
||||
}
|
||||
svc := NewSettingService(repo, &config.Config{
|
||||
WeChat: config.WeChatConnectConfig{
|
||||
Enabled: true,
|
||||
OpenEnabled: true,
|
||||
MPEnabled: true,
|
||||
Mode: "open",
|
||||
OpenAppID: "wx-open-config",
|
||||
OpenAppSecret: "wx-open-secret",
|
||||
MPAppID: "wx-mp-config",
|
||||
MPAppSecret: "wx-mp-secret",
|
||||
FrontendRedirectURL: "/auth/wechat/config-callback",
|
||||
},
|
||||
})
|
||||
|
||||
got, err := svc.GetWeChatConnectOAuthConfig(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.True(t, got.Enabled)
|
||||
require.True(t, got.OpenEnabled)
|
||||
require.True(t, got.MPEnabled)
|
||||
require.Equal(t, "wx-open-config", got.AppIDForMode("open"))
|
||||
require.Equal(t, "wx-mp-config", got.AppIDForMode("mp"))
|
||||
}
|
||||
|
||||
func TestSettingService_ParseSettings_FallsBackToConfigForWeChatAdminView(t *testing.T) {
|
||||
svc := NewSettingService(&settingWeChatRepoStub{values: map[string]string{}}, &config.Config{
|
||||
WeChat: config.WeChatConnectConfig{
|
||||
|
||||
Reference in New Issue
Block a user