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
0934f737
"frontend/src/api/vscode:/vscode.git/clone" did not exist on "11c06803acca65bec6d583c9650ddd0d87abe11a"
Commit
0934f737
authored
Apr 21, 2026
by
IanShaw027
Browse files
fix: snapshot merchant identity for alipay and easypay
parent
267844eb
Changes
13
Hide whitespace changes
Inline
Side-by-side
backend/internal/payment/provider/alipay.go
View file @
0934f737
...
...
@@ -91,6 +91,17 @@ func (a *Alipay) SupportedTypes() []payment.PaymentType {
return
[]
payment
.
PaymentType
{
payment
.
TypeAlipay
}
}
func
(
a
*
Alipay
)
MerchantIdentityMetadata
()
map
[
string
]
string
{
if
a
==
nil
{
return
nil
}
appID
:=
strings
.
TrimSpace
(
a
.
config
[
"appId"
])
if
appID
==
""
{
return
nil
}
return
map
[
string
]
string
{
"app_id"
:
appID
}
}
// CreatePayment creates an Alipay payment page URL.
func
(
a
*
Alipay
)
CreatePayment
(
ctx
context
.
Context
,
req
payment
.
CreatePaymentRequest
)
(
*
payment
.
CreatePaymentResponse
,
error
)
{
client
,
err
:=
a
.
getClient
()
...
...
@@ -181,10 +192,11 @@ func (a *Alipay) QueryOrder(ctx context.Context, tradeNo string) (*payment.Query
}
return
&
payment
.
QueryOrderResponse
{
TradeNo
:
result
.
TradeNo
,
Status
:
status
,
Amount
:
amount
,
PaidAt
:
result
.
SendPayDate
,
TradeNo
:
result
.
TradeNo
,
Status
:
status
,
Amount
:
amount
,
PaidAt
:
result
.
SendPayDate
,
Metadata
:
a
.
MerchantIdentityMetadata
(),
},
nil
}
...
...
@@ -215,12 +227,21 @@ func (a *Alipay) VerifyNotification(ctx context.Context, rawBody string, _ map[s
return
nil
,
fmt
.
Errorf
(
"alipay parse notification amount %q: %w"
,
notification
.
TotalAmount
,
err
)
}
metadata
:=
a
.
MerchantIdentityMetadata
()
if
appID
:=
strings
.
TrimSpace
(
notification
.
AppId
);
appID
!=
""
{
if
metadata
==
nil
{
metadata
=
map
[
string
]
string
{}
}
metadata
[
"app_id"
]
=
appID
}
return
&
payment
.
PaymentNotification
{
TradeNo
:
notification
.
TradeNo
,
OrderID
:
notification
.
OutTradeNo
,
Amount
:
amount
,
Status
:
status
,
RawData
:
rawBody
,
TradeNo
:
notification
.
TradeNo
,
OrderID
:
notification
.
OutTradeNo
,
Amount
:
amount
,
Status
:
status
,
RawData
:
rawBody
,
Metadata
:
metadata
,
},
nil
}
...
...
@@ -283,6 +304,7 @@ func isTradeNotExist(err error) bool {
// Ensure interface compliance.
var
(
_
payment
.
Provider
=
(
*
Alipay
)(
nil
)
_
payment
.
CancelableProvider
=
(
*
Alipay
)(
nil
)
_
payment
.
Provider
=
(
*
Alipay
)(
nil
)
_
payment
.
CancelableProvider
=
(
*
Alipay
)(
nil
)
_
payment
.
MerchantIdentityProvider
=
(
*
Alipay
)(
nil
)
)
backend/internal/payment/provider/alipay_test.go
View file @
0934f737
...
...
@@ -243,3 +243,18 @@ func TestCreateTradeUsesWapPayForMobile(t *testing.T) {
t
.
Fatalf
(
"qr_code = %q, want empty"
,
resp
.
QRCode
)
}
}
func
TestAlipayMerchantIdentityMetadata
(
t
*
testing
.
T
)
{
t
.
Parallel
()
provider
:=
&
Alipay
{
config
:
map
[
string
]
string
{
"appId"
:
"2021001234567890"
,
},
}
metadata
:=
provider
.
MerchantIdentityMetadata
()
if
metadata
[
"app_id"
]
!=
"2021001234567890"
{
t
.
Fatalf
(
"app_id = %q, want %q"
,
metadata
[
"app_id"
],
"2021001234567890"
)
}
}
backend/internal/payment/provider/easypay.go
View file @
0934f737
...
...
@@ -59,6 +59,17 @@ func (e *EasyPay) SupportedTypes() []payment.PaymentType {
return
[]
payment
.
PaymentType
{
payment
.
TypeAlipay
,
payment
.
TypeWxpay
}
}
func
(
e
*
EasyPay
)
MerchantIdentityMetadata
()
map
[
string
]
string
{
if
e
==
nil
{
return
nil
}
pid
:=
strings
.
TrimSpace
(
e
.
config
[
"pid"
])
if
pid
==
""
{
return
nil
}
return
map
[
string
]
string
{
"pid"
:
pid
}
}
func
(
e
*
EasyPay
)
CreatePayment
(
ctx
context
.
Context
,
req
payment
.
CreatePaymentRequest
)
(
*
payment
.
CreatePaymentResponse
,
error
)
{
// Payment mode determined by instance config, not payment type.
// "popup" → hosted page (submit.php); "qrcode"/default → API call (mapi.php).
...
...
@@ -178,7 +189,12 @@ func (e *EasyPay) QueryOrder(ctx context.Context, tradeNo string) (*payment.Quer
status
=
payment
.
ProviderStatusPaid
}
amount
,
_
:=
strconv
.
ParseFloat
(
resp
.
Money
,
64
)
return
&
payment
.
QueryOrderResponse
{
TradeNo
:
tradeNo
,
Status
:
status
,
Amount
:
amount
},
nil
return
&
payment
.
QueryOrderResponse
{
TradeNo
:
tradeNo
,
Status
:
status
,
Amount
:
amount
,
Metadata
:
e
.
MerchantIdentityMetadata
(),
},
nil
}
func
(
e
*
EasyPay
)
VerifyNotification
(
_
context
.
Context
,
rawBody
string
,
_
map
[
string
]
string
)
(
*
payment
.
PaymentNotification
,
error
)
{
...
...
@@ -203,9 +219,17 @@ func (e *EasyPay) VerifyNotification(_ context.Context, rawBody string, _ map[st
status
=
payment
.
ProviderStatusSuccess
}
amount
,
_
:=
strconv
.
ParseFloat
(
params
[
"money"
],
64
)
metadata
:=
e
.
MerchantIdentityMetadata
()
if
pid
:=
strings
.
TrimSpace
(
params
[
"pid"
]);
pid
!=
""
{
if
metadata
==
nil
{
metadata
=
map
[
string
]
string
{}
}
metadata
[
"pid"
]
=
pid
}
return
&
payment
.
PaymentNotification
{
TradeNo
:
params
[
"trade_no"
],
OrderID
:
params
[
"out_trade_no"
],
Amount
:
amount
,
Status
:
status
,
RawData
:
rawBody
,
Amount
:
amount
,
Status
:
status
,
RawData
:
rawBody
,
Metadata
:
metadata
,
},
nil
}
...
...
backend/internal/payment/provider/easypay_sign_test.go
View file @
0934f737
...
...
@@ -178,3 +178,18 @@ func TestEasyPayVerifySignWrongSignValue(t *testing.T) {
t
.
Fatal
(
"easyPayVerifySign should return false for an incorrect sign value"
)
}
}
func
TestEasyPayMerchantIdentityMetadata
(
t
*
testing
.
T
)
{
t
.
Parallel
()
provider
:=
&
EasyPay
{
config
:
map
[
string
]
string
{
"pid"
:
"1001"
,
},
}
metadata
:=
provider
.
MerchantIdentityMetadata
()
if
metadata
[
"pid"
]
!=
"1001"
{
t
.
Fatalf
(
"pid = %q, want %q"
,
metadata
[
"pid"
],
"1001"
)
}
}
backend/internal/payment/provider/wxpay_test.go
View file @
0934f737
...
...
@@ -110,7 +110,7 @@ func TestBuildWxpayTransactionMetadata(t *testing.T) {
Appid
:
strPtr
(
"wx-app-id"
),
Mchid
:
strPtr
(
"mch-id"
),
TradeState
:
strPtr
(
wxpayTradeStateSuccess
),
Amount
:
&
payments
.
Amount
{
Amount
:
&
payments
.
Transaction
Amount
{
Currency
:
strPtr
(
wxpayCurrency
),
},
}
...
...
backend/internal/payment/types.go
View file @
0934f737
...
...
@@ -214,3 +214,9 @@ type CancelableProvider interface {
// CancelPayment cancels/expires a pending payment on the upstream platform.
CancelPayment
(
ctx
context
.
Context
,
tradeNo
string
)
error
}
// MerchantIdentityProvider exposes the current non-sensitive merchant identity
// derived from provider configuration for snapshot consistency checks.
type
MerchantIdentityProvider
interface
{
MerchantIdentityMetadata
()
map
[
string
]
string
}
backend/internal/service/payment_fulfillment.go
View file @
0934f737
...
...
@@ -96,47 +96,7 @@ func (s *PaymentService) confirmPayment(ctx context.Context, oid int64, tradeNo
}
func
validateProviderNotificationMetadata
(
order
*
dbent
.
PaymentOrder
,
providerKey
string
,
metadata
map
[
string
]
string
)
error
{
if
order
==
nil
||
len
(
metadata
)
==
0
||
!
strings
.
EqualFold
(
strings
.
TrimSpace
(
providerKey
),
payment
.
TypeWxpay
)
{
return
nil
}
snapshot
:=
psOrderProviderSnapshot
(
order
)
if
snapshot
==
nil
{
return
nil
}
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
MerchantAppID
);
expected
!=
""
{
actual
:=
strings
.
TrimSpace
(
metadata
[
"appid"
])
if
actual
==
""
{
return
fmt
.
Errorf
(
"wxpay notification missing appid"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"wxpay appid mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
MerchantID
);
expected
!=
""
{
actual
:=
strings
.
TrimSpace
(
metadata
[
"mchid"
])
if
actual
==
""
{
return
fmt
.
Errorf
(
"wxpay notification missing mchid"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"wxpay mchid mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
Currency
);
expected
!=
""
{
actual
:=
strings
.
ToUpper
(
strings
.
TrimSpace
(
metadata
[
"currency"
]))
if
actual
==
""
{
return
fmt
.
Errorf
(
"wxpay notification missing currency"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"wxpay currency mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
if
actual
:=
strings
.
TrimSpace
(
metadata
[
"trade_state"
]);
actual
!=
""
&&
!
strings
.
EqualFold
(
actual
,
"SUCCESS"
)
{
return
fmt
.
Errorf
(
"wxpay trade_state mismatch: expected SUCCESS, got %s"
,
actual
)
}
return
nil
return
validateProviderSnapshotMetadata
(
order
,
providerKey
,
metadata
)
}
func
expectedNotificationProviderKey
(
registry
*
payment
.
Registry
,
orderPaymentType
string
,
orderProviderKey
string
,
instanceProviderKey
string
)
string
{
...
...
backend/internal/service/payment_fulfillment_test.go
View file @
0934f737
...
...
@@ -321,3 +321,37 @@ func TestParseLegacyPaymentOrderID(t *testing.T) {
_
,
ok
=
parseLegacyPaymentOrderID
(
"sub2_42"
,
errors
.
New
(
"db down"
))
assert
.
False
(
t
,
ok
)
}
func
TestValidateProviderNotificationMetadataRejectsAlipaySnapshotMismatch
(
t
*
testing
.
T
)
{
t
.
Parallel
()
order
:=
&
dbent
.
PaymentOrder
{
PaymentType
:
payment
.
TypeAlipay
,
ProviderSnapshot
:
map
[
string
]
any
{
"schema_version"
:
2
,
"merchant_app_id"
:
"alipay-app-expected"
,
},
}
err
:=
validateProviderNotificationMetadata
(
order
,
payment
.
TypeAlipay
,
map
[
string
]
string
{
"app_id"
:
"alipay-app-other"
,
})
assert
.
ErrorContains
(
t
,
err
,
"alipay app_id mismatch"
)
}
func
TestValidateProviderNotificationMetadataRejectsEasyPaySnapshotMismatch
(
t
*
testing
.
T
)
{
t
.
Parallel
()
order
:=
&
dbent
.
PaymentOrder
{
PaymentType
:
payment
.
TypeAlipay
,
ProviderSnapshot
:
map
[
string
]
any
{
"schema_version"
:
2
,
"merchant_id"
:
"pid-expected"
,
},
}
err
:=
validateProviderNotificationMetadata
(
order
,
payment
.
TypeEasyPay
,
map
[
string
]
string
{
"pid"
:
"pid-other"
,
})
assert
.
ErrorContains
(
t
,
err
,
"easypay pid mismatch"
)
}
backend/internal/service/payment_order.go
View file @
0934f737
...
...
@@ -240,6 +240,16 @@ func buildPaymentOrderProviderSnapshot(sel *payment.InstanceSelection, req Creat
}
snapshot
[
"currency"
]
=
"CNY"
}
if
providerKey
==
payment
.
TypeAlipay
{
if
merchantAppID
:=
strings
.
TrimSpace
(
sel
.
Config
[
"appId"
]);
merchantAppID
!=
""
{
snapshot
[
"merchant_app_id"
]
=
merchantAppID
}
}
if
providerKey
==
payment
.
TypeEasyPay
{
if
merchantID
:=
strings
.
TrimSpace
(
sel
.
Config
[
"pid"
]);
merchantID
!=
""
{
snapshot
[
"merchant_id"
]
=
merchantID
}
}
if
len
(
snapshot
)
==
1
{
return
nil
...
...
backend/internal/service/payment_order_provider_snapshot.go
View file @
0934f737
...
...
@@ -125,3 +125,81 @@ func expectedNotificationProviderKeyForOrder(registry *payment.Registry, order *
return
expectedNotificationProviderKey
(
registry
,
order
.
PaymentType
,
orderProviderKey
,
instanceProviderKey
)
}
func
validateProviderSnapshotMetadata
(
order
*
dbent
.
PaymentOrder
,
providerKey
string
,
metadata
map
[
string
]
string
)
error
{
if
order
==
nil
||
len
(
metadata
)
==
0
{
return
nil
}
snapshot
:=
psOrderProviderSnapshot
(
order
)
if
snapshot
==
nil
{
return
nil
}
switch
strings
.
TrimSpace
(
providerKey
)
{
case
payment
.
TypeWxpay
:
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
MerchantAppID
);
expected
!=
""
{
actual
:=
strings
.
TrimSpace
(
metadata
[
"appid"
])
if
actual
==
""
{
return
fmt
.
Errorf
(
"wxpay notification missing appid"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"wxpay appid mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
MerchantID
);
expected
!=
""
{
actual
:=
strings
.
TrimSpace
(
metadata
[
"mchid"
])
if
actual
==
""
{
return
fmt
.
Errorf
(
"wxpay notification missing mchid"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"wxpay mchid mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
Currency
);
expected
!=
""
{
actual
:=
strings
.
ToUpper
(
strings
.
TrimSpace
(
metadata
[
"currency"
]))
if
actual
==
""
{
return
fmt
.
Errorf
(
"wxpay notification missing currency"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"wxpay currency mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
if
actual
:=
strings
.
TrimSpace
(
metadata
[
"trade_state"
]);
actual
!=
""
&&
!
strings
.
EqualFold
(
actual
,
"SUCCESS"
)
{
return
fmt
.
Errorf
(
"wxpay trade_state mismatch: expected SUCCESS, got %s"
,
actual
)
}
case
payment
.
TypeAlipay
:
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
MerchantAppID
);
expected
!=
""
{
actual
:=
strings
.
TrimSpace
(
metadata
[
"app_id"
])
if
actual
==
""
{
return
fmt
.
Errorf
(
"alipay app_id missing"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"alipay app_id mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
case
payment
.
TypeEasyPay
:
if
expected
:=
strings
.
TrimSpace
(
snapshot
.
MerchantID
);
expected
!=
""
{
actual
:=
strings
.
TrimSpace
(
metadata
[
"pid"
])
if
actual
==
""
{
return
fmt
.
Errorf
(
"easypay pid missing"
)
}
if
!
strings
.
EqualFold
(
expected
,
actual
)
{
return
fmt
.
Errorf
(
"easypay pid mismatch: expected %s, got %s"
,
expected
,
actual
)
}
}
}
return
nil
}
func
providerMerchantIdentityMetadata
(
prov
payment
.
Provider
)
map
[
string
]
string
{
if
prov
==
nil
{
return
nil
}
reporter
,
ok
:=
prov
.
(
payment
.
MerchantIdentityProvider
)
if
!
ok
{
return
nil
}
return
reporter
.
MerchantIdentityMetadata
()
}
backend/internal/service/payment_order_provider_snapshot_test.go
View file @
0934f737
...
...
@@ -130,6 +130,40 @@ func TestBuildPaymentOrderProviderSnapshot_UsesWxpayJSAPIAppIDForOpenIDOrders(t
require
.
Equal
(
t
,
"CNY"
,
snapshot
[
"currency"
])
}
func
TestBuildPaymentOrderProviderSnapshot_IncludesAlipayMerchantIdentity
(
t
*
testing
.
T
)
{
t
.
Parallel
()
snapshot
:=
buildPaymentOrderProviderSnapshot
(
&
payment
.
InstanceSelection
{
InstanceID
:
"21"
,
ProviderKey
:
payment
.
TypeAlipay
,
Config
:
map
[
string
]
string
{
"appId"
:
"alipay-app-21"
,
"privateKey"
:
"secret"
,
},
PaymentMode
:
"redirect"
,
},
CreateOrderRequest
{})
require
.
Equal
(
t
,
"alipay-app-21"
,
snapshot
[
"merchant_app_id"
])
require
.
NotContains
(
t
,
snapshot
,
"privateKey"
)
}
func
TestBuildPaymentOrderProviderSnapshot_IncludesEasyPayMerchantIdentity
(
t
*
testing
.
T
)
{
t
.
Parallel
()
snapshot
:=
buildPaymentOrderProviderSnapshot
(
&
payment
.
InstanceSelection
{
InstanceID
:
"66"
,
ProviderKey
:
payment
.
TypeEasyPay
,
Config
:
map
[
string
]
string
{
"pid"
:
"easypay-merchant-66"
,
"pkey"
:
"secret"
,
},
PaymentMode
:
"popup"
,
},
CreateOrderRequest
{
PaymentType
:
payment
.
TypeAlipay
})
require
.
Equal
(
t
,
"easypay-merchant-66"
,
snapshot
[
"merchant_id"
])
require
.
NotContains
(
t
,
snapshot
,
"pkey"
)
}
func
valueOrEmpty
(
v
*
string
)
string
{
if
v
==
nil
{
return
""
...
...
backend/internal/service/payment_refund.go
View file @
0934f737
...
...
@@ -333,6 +333,12 @@ func (s *PaymentService) gwRefund(ctx context.Context, p *RefundPlan) error {
if
err
!=
nil
{
return
fmt
.
Errorf
(
"get refund provider: %w"
,
err
)
}
if
err
:=
validateProviderSnapshotMetadata
(
p
.
Order
,
prov
.
ProviderKey
(),
providerMerchantIdentityMetadata
(
prov
));
err
!=
nil
{
s
.
writeAuditLog
(
ctx
,
p
.
Order
.
ID
,
"REFUND_PROVIDER_METADATA_MISMATCH"
,
"admin"
,
map
[
string
]
any
{
"detail"
:
err
.
Error
(),
})
return
err
}
_
,
err
=
prov
.
Refund
(
ctx
,
payment
.
RefundRequest
{
TradeNo
:
p
.
Order
.
PaymentTradeNo
,
OrderID
:
p
.
Order
.
OutTradeNo
,
...
...
backend/internal/service/payment_refund_test.go
View file @
0934f737
...
...
@@ -4,6 +4,7 @@ package service
import
(
"context"
"strconv"
"testing"
"time"
...
...
@@ -115,3 +116,71 @@ func TestPrepareRefundRejectsLegacyGuessedProviderInstance(t *testing.T) {
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
"REFUND_DISABLED"
,
infraerrors
.
Reason
(
err
))
}
func
TestGwRefundRejectsAlipayMerchantIdentitySnapshotMismatch
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
user
,
err
:=
client
.
User
.
Create
()
.
SetEmail
(
"refund-snapshot-mismatch@example.com"
)
.
SetPasswordHash
(
"hash"
)
.
SetUsername
(
"refund-snapshot-mismatch-user"
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
inst
,
err
:=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeAlipay
)
.
SetName
(
"alipay-refund-mismatch-instance"
)
.
SetConfig
(
encryptWebhookProviderConfig
(
t
,
map
[
string
]
string
{
"appId"
:
"runtime-alipay-app"
,
"privateKey"
:
"runtime-private-key"
,
}))
.
SetSupportedTypes
(
"alipay"
)
.
SetEnabled
(
true
)
.
SetRefundEnabled
(
true
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
instID
:=
strconv
.
FormatInt
(
inst
.
ID
,
10
)
order
,
err
:=
client
.
PaymentOrder
.
Create
()
.
SetUserID
(
user
.
ID
)
.
SetUserEmail
(
user
.
Email
)
.
SetUserName
(
user
.
Username
)
.
SetAmount
(
88
)
.
SetPayAmount
(
88
)
.
SetFeeRate
(
0
)
.
SetRechargeCode
(
"REFUND-SNAPSHOT-MISMATCH-ORDER"
)
.
SetOutTradeNo
(
"sub2_refund_snapshot_mismatch_order"
)
.
SetPaymentType
(
payment
.
TypeAlipay
)
.
SetPaymentTradeNo
(
"trade-refund-snapshot-mismatch"
)
.
SetOrderType
(
payment
.
OrderTypeBalance
)
.
SetStatus
(
OrderStatusCompleted
)
.
SetExpiresAt
(
time
.
Now
()
.
Add
(
time
.
Hour
))
.
SetPaidAt
(
time
.
Now
())
.
SetClientIP
(
"127.0.0.1"
)
.
SetSrcHost
(
"api.example.com"
)
.
SetProviderInstanceID
(
instID
)
.
SetProviderKey
(
payment
.
TypeAlipay
)
.
SetProviderSnapshot
(
map
[
string
]
any
{
"schema_version"
:
2
,
"provider_instance_id"
:
instID
,
"provider_key"
:
payment
.
TypeAlipay
,
"merchant_app_id"
:
"expected-alipay-app"
,
})
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
svc
:=
&
PaymentService
{
entClient
:
client
,
loadBalancer
:
newWebhookProviderTestLoadBalancer
(
client
),
}
err
=
svc
.
gwRefund
(
ctx
,
&
RefundPlan
{
OrderID
:
order
.
ID
,
Order
:
order
,
RefundAmount
:
order
.
Amount
,
GatewayAmount
:
order
.
Amount
,
Reason
:
"snapshot mismatch"
,
})
require
.
ErrorContains
(
t
,
err
,
"alipay app_id mismatch"
)
}
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