Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
9d5e9bbc
Commit
9d5e9bbc
authored
Apr 22, 2026
by
IanShaw027
Browse files
fix(payment): respect configured visible method source
parent
45487322
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/internal/service/payment_order_jsapi_test.go
View file @
9d5e9bbc
...
@@ -31,3 +31,68 @@ func TestUsesOfficialWxpayVisibleMethodDerivesFromEnabledProviderInstance(t *tes
...
@@ -31,3 +31,68 @@ func TestUsesOfficialWxpayVisibleMethodDerivesFromEnabledProviderInstance(t *tes
t
.
Fatal
(
"expected official wxpay visible method to be detected from enabled provider instance"
)
t
.
Fatal
(
"expected official wxpay visible method to be detected from enabled provider instance"
)
}
}
}
}
func
TestUsesOfficialWxpayVisibleMethodRespectsConfiguredSourceWhenMultipleProvidersEnabled
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
source
string
wantOfficial
bool
}{
{
name
:
"official source selected"
,
source
:
VisibleMethodSourceOfficialWechat
,
wantOfficial
:
true
,
},
{
name
:
"easypay source selected"
,
source
:
VisibleMethodSourceEasyPayWechat
,
wantOfficial
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
_
,
err
:=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeWxpay
)
.
SetName
(
"Official WeChat"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"wxpay"
)
.
SetEnabled
(
true
)
.
SetSortOrder
(
1
)
.
Save
(
ctx
)
if
err
!=
nil
{
t
.
Fatalf
(
"create official wxpay instance: %v"
,
err
)
}
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeEasyPay
)
.
SetName
(
"EasyPay WeChat"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"wxpay"
)
.
SetEnabled
(
true
)
.
SetSortOrder
(
2
)
.
Save
(
ctx
)
if
err
!=
nil
{
t
.
Fatalf
(
"create easypay wxpay instance: %v"
,
err
)
}
svc
:=
&
PaymentService
{
configService
:
&
PaymentConfigService
{
entClient
:
client
,
settingRepo
:
&
paymentConfigSettingRepoStub
{
values
:
map
[
string
]
string
{
SettingPaymentVisibleMethodWxpaySource
:
tt
.
source
,
},
},
},
}
if
got
:=
svc
.
usesOfficialWxpayVisibleMethod
(
ctx
);
got
!=
tt
.
wantOfficial
{
t
.
Fatalf
(
"usesOfficialWxpayVisibleMethod() = %v, want %v"
,
got
,
tt
.
wantOfficial
)
}
})
}
}
backend/internal/service/payment_resume_service_test.go
View file @
9d5e9bbc
...
@@ -14,6 +14,7 @@ import (
...
@@ -14,6 +14,7 @@ import (
"time"
"time"
"github.com/Wei-Shaw/sub2api/internal/payment"
"github.com/Wei-Shaw/sub2api/internal/payment"
infraerrors
"github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
)
func
TestNormalizeVisibleMethods
(
t
*
testing
.
T
)
{
func
TestNormalizeVisibleMethods
(
t
*
testing
.
T
)
{
...
@@ -419,6 +420,211 @@ func TestVisibleMethodLoadBalancerUsesEnabledProviderInstance(t *testing.T) {
...
@@ -419,6 +420,211 @@ func TestVisibleMethodLoadBalancerUsesEnabledProviderInstance(t *testing.T) {
}
}
}
}
func
TestVisibleMethodLoadBalancerUsesConfiguredSourceWhenMultipleProvidersEnabled
(
t
*
testing
.
T
)
{
t
.
Parallel
()
tests
:=
[]
struct
{
name
string
method
payment
.
PaymentType
officialName
string
officialTypes
string
easyPayName
string
easyPayTypes
string
sourceSetting
string
wantProvider
string
}{
{
name
:
"alipay uses official source"
,
method
:
payment
.
TypeAlipay
,
officialName
:
"Official Alipay"
,
officialTypes
:
"alipay"
,
easyPayName
:
"EasyPay Alipay"
,
easyPayTypes
:
"alipay"
,
sourceSetting
:
VisibleMethodSourceOfficialAlipay
,
wantProvider
:
payment
.
TypeAlipay
,
},
{
name
:
"alipay uses easypay source"
,
method
:
payment
.
TypeAlipay
,
officialName
:
"Official Alipay"
,
officialTypes
:
"alipay"
,
easyPayName
:
"EasyPay Alipay"
,
easyPayTypes
:
"alipay"
,
sourceSetting
:
VisibleMethodSourceEasyPayAlipay
,
wantProvider
:
payment
.
TypeEasyPay
,
},
{
name
:
"wxpay uses official source"
,
method
:
payment
.
TypeWxpay
,
officialName
:
"Official WeChat"
,
officialTypes
:
"wxpay"
,
easyPayName
:
"EasyPay WeChat"
,
easyPayTypes
:
"wxpay"
,
sourceSetting
:
VisibleMethodSourceOfficialWechat
,
wantProvider
:
payment
.
TypeWxpay
,
},
{
name
:
"wxpay uses easypay source"
,
method
:
payment
.
TypeWxpay
,
officialName
:
"Official WeChat"
,
officialTypes
:
"wxpay"
,
easyPayName
:
"EasyPay WeChat"
,
easyPayTypes
:
"wxpay"
,
sourceSetting
:
VisibleMethodSourceEasyPayWechat
,
wantProvider
:
payment
.
TypeEasyPay
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Parallel
()
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
officialProviderKey
:=
payment
.
TypeAlipay
if
tt
.
method
==
payment
.
TypeWxpay
{
officialProviderKey
=
payment
.
TypeWxpay
}
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
officialProviderKey
)
.
SetName
(
tt
.
officialName
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
tt
.
officialTypes
)
.
SetEnabled
(
true
)
.
SetSortOrder
(
1
)
.
Save
(
ctx
)
if
err
!=
nil
{
t
.
Fatalf
(
"create official provider: %v"
,
err
)
}
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeEasyPay
)
.
SetName
(
tt
.
easyPayName
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
tt
.
easyPayTypes
)
.
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
(
tt
.
method
)
:
tt
.
sourceSetting
,
},
},
}
lb
:=
newVisibleMethodLoadBalancer
(
inner
,
configService
)
_
,
err
=
lb
.
SelectInstance
(
ctx
,
""
,
tt
.
method
,
payment
.
StrategyRoundRobin
,
12.5
)
if
err
!=
nil
{
t
.
Fatalf
(
"SelectInstance returned error: %v"
,
err
)
}
if
inner
.
lastProviderKey
!=
tt
.
wantProvider
{
t
.
Fatalf
(
"lastProviderKey = %q, want %q"
,
inner
.
lastProviderKey
,
tt
.
wantProvider
)
}
})
}
}
func
TestVisibleMethodLoadBalancerRejectsMissingOrInvalidSourceWhenMultipleProvidersEnabled
(
t
*
testing
.
T
)
{
t
.
Parallel
()
tests
:=
[]
struct
{
name
string
method
payment
.
PaymentType
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
,
sourceValue
:
"stripe"
,
wantMessage
:
"wxpay source must be one of the supported payment providers"
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Parallel
()
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
officialProviderKey
:=
payment
.
TypeAlipay
officialSupportedTypes
:=
"alipay"
officialName
:=
"Official Alipay"
easyPaySupportedTypes
:=
"alipay"
easyPayName
:=
"EasyPay Alipay"
if
tt
.
method
==
payment
.
TypeWxpay
{
officialProviderKey
=
payment
.
TypeWxpay
officialSupportedTypes
=
"wxpay"
officialName
=
"Official WeChat"
easyPaySupportedTypes
=
"wxpay"
easyPayName
=
"EasyPay WeChat"
}
_
,
err
:=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
officialProviderKey
)
.
SetName
(
officialName
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
officialSupportedTypes
)
.
SetEnabled
(
true
)
.
SetSortOrder
(
1
)
.
Save
(
ctx
)
if
err
!=
nil
{
t
.
Fatalf
(
"create official provider: %v"
,
err
)
}
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeEasyPay
)
.
SetName
(
easyPayName
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
easyPaySupportedTypes
)
.
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
(
tt
.
method
)
:
tt
.
sourceValue
,
},
},
}
lb
:=
newVisibleMethodLoadBalancer
(
inner
,
configService
)
_
,
err
=
lb
.
SelectInstance
(
ctx
,
""
,
tt
.
method
,
payment
.
StrategyRoundRobin
,
9.9
)
if
err
==
nil
{
t
.
Fatal
(
"SelectInstance should reject invalid visible method source configuration"
)
}
if
infraerrors
.
Reason
(
err
)
!=
"INVALID_PAYMENT_VISIBLE_METHOD_SOURCE"
{
t
.
Fatalf
(
"Reason(err) = %q, want %q"
,
infraerrors
.
Reason
(
err
),
"INVALID_PAYMENT_VISIBLE_METHOD_SOURCE"
)
}
if
infraerrors
.
Message
(
err
)
!=
tt
.
wantMessage
{
t
.
Fatalf
(
"Message(err) = %q, want %q"
,
infraerrors
.
Message
(
err
),
tt
.
wantMessage
)
}
})
}
}
func
TestVisibleMethodLoadBalancerRejectsMissingEnabledVisibleMethodProvider
(
t
*
testing
.
T
)
{
func
TestVisibleMethodLoadBalancerRejectsMissingEnabledVisibleMethodProvider
(
t
*
testing
.
T
)
{
t
.
Parallel
()
t
.
Parallel
()
...
...
backend/internal/service/payment_visible_method_instances.go
View file @
9d5e9bbc
...
@@ -82,6 +82,19 @@ func filterEnabledVisibleMethodInstances(instances []*dbent.PaymentProviderInsta
...
@@ -82,6 +82,19 @@ func filterEnabledVisibleMethodInstances(instances []*dbent.PaymentProviderInsta
return
filtered
return
filtered
}
}
func
selectVisibleMethodInstanceByProviderKey
(
instances
[]
*
dbent
.
PaymentProviderInstance
,
providerKey
string
)
*
dbent
.
PaymentProviderInstance
{
providerKey
=
strings
.
TrimSpace
(
providerKey
)
if
providerKey
==
""
{
return
nil
}
for
_
,
inst
:=
range
instances
{
if
strings
.
EqualFold
(
strings
.
TrimSpace
(
inst
.
ProviderKey
),
providerKey
)
{
return
inst
}
}
return
nil
}
func
buildPaymentProviderConflictError
(
method
string
,
conflicting
*
dbent
.
PaymentProviderInstance
)
error
{
func
buildPaymentProviderConflictError
(
method
string
,
conflicting
*
dbent
.
PaymentProviderInstance
)
error
{
metadata
:=
map
[
string
]
string
{
metadata
:=
map
[
string
]
string
{
"payment_method"
:
NormalizeVisibleMethod
(
method
),
"payment_method"
:
NormalizeVisibleMethod
(
method
),
...
@@ -133,6 +146,32 @@ func (s *PaymentConfigService) validateVisibleMethodEnablementConflicts(
...
@@ -133,6 +146,32 @@ func (s *PaymentConfigService) validateVisibleMethodEnablementConflicts(
return
nil
return
nil
}
}
func
(
s
*
PaymentConfigService
)
resolveVisibleMethodSourceProviderKey
(
ctx
context
.
Context
,
method
string
)
(
string
,
error
)
{
method
=
NormalizeVisibleMethod
(
method
)
sourceKey
:=
visibleMethodSourceSettingKey
(
method
)
rawSource
:=
""
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
)
}
rawSource
=
value
}
normalizedSource
,
err
:=
normalizeVisibleMethodSettingSource
(
method
,
rawSource
,
true
)
if
err
!=
nil
{
return
""
,
err
}
providerKey
,
ok
:=
VisibleMethodProviderKeyForSource
(
method
,
normalizedSource
)
if
!
ok
{
return
""
,
infraerrors
.
BadRequest
(
"INVALID_PAYMENT_VISIBLE_METHOD_SOURCE"
,
fmt
.
Sprintf
(
"%s source must be one of the supported payment providers"
,
method
),
)
}
return
providerKey
,
nil
}
func
(
s
*
PaymentConfigService
)
resolveEnabledVisibleMethodInstance
(
func
(
s
*
PaymentConfigService
)
resolveEnabledVisibleMethodInstance
(
ctx
context
.
Context
,
ctx
context
.
Context
,
method
string
,
method
string
,
...
@@ -161,6 +200,17 @@ func (s *PaymentConfigService) resolveEnabledVisibleMethodInstance(
...
@@ -161,6 +200,17 @@ func (s *PaymentConfigService) resolveEnabledVisibleMethodInstance(
case
1
:
case
1
:
return
matching
[
0
],
nil
return
matching
[
0
],
nil
default
:
default
:
return
nil
,
buildPaymentProviderConflictError
(
method
,
matching
[
0
])
providerKey
,
err
:=
s
.
resolveVisibleMethodSourceProviderKey
(
ctx
,
method
)
if
err
!=
nil
{
return
nil
,
err
}
selected
:=
selectVisibleMethodInstanceByProviderKey
(
matching
,
providerKey
)
if
selected
==
nil
{
return
nil
,
infraerrors
.
BadRequest
(
"INVALID_PAYMENT_VISIBLE_METHOD_SOURCE"
,
fmt
.
Sprintf
(
"%s source has no enabled provider instance"
,
method
),
)
}
return
selected
,
nil
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment