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
07f23aaa
"vscode:/vscode.git/clone" did not exist on "3bd3027251dd5e008e60ba7299ef63dbc8f8a32c"
Commit
07f23aaa
authored
Apr 21, 2026
by
IanShaw027
Browse files
fix wxpay config contract and h5 scene info
parent
2626e8f2
Changes
6
Show whitespace changes
Inline
Side-by-side
backend/internal/payment/provider/wxpay.go
View file @
07f23aaa
...
...
@@ -250,13 +250,12 @@ func (w *Wxpay) prepayNative(ctx context.Context, c *core.Client, req payment.Cr
func
(
w
*
Wxpay
)
prepayH5
(
ctx
context
.
Context
,
c
*
core
.
Client
,
req
payment
.
CreatePaymentRequest
,
notifyURL
string
,
totalFen
int64
)
(
*
payment
.
CreatePaymentResponse
,
error
)
{
svc
:=
h5
.
H5ApiService
{
Client
:
c
}
cur
:=
wxpayCurrency
tp
:=
wxpayH5Type
resp
,
_
,
err
:=
wxpayH5Prepay
(
ctx
,
svc
,
h5
.
PrepayRequest
{
Appid
:
core
.
String
(
w
.
config
[
"appId"
]),
Mchid
:
core
.
String
(
w
.
config
[
"mchId"
]),
Description
:
core
.
String
(
req
.
Subject
),
OutTradeNo
:
core
.
String
(
req
.
OrderID
),
NotifyUrl
:
core
.
String
(
notifyURL
),
Amount
:
&
h5
.
Amount
{
Total
:
core
.
Int64
(
totalFen
),
Currency
:
&
cur
},
SceneInfo
:
&
h5
.
SceneInfo
{
PayerClientIp
:
core
.
String
(
req
.
ClientIP
),
H5Info
:
&
h5
.
H5Info
{
Type
:
&
tp
}
},
SceneInfo
:
&
h5
.
SceneInfo
{
PayerClientIp
:
core
.
String
(
req
.
ClientIP
),
H5Info
:
buildWxpayH5Info
(
w
.
config
)
},
})
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"wxpay h5 prepay: %w"
,
err
)
...
...
@@ -272,6 +271,18 @@ func (w *Wxpay) prepayH5(ctx context.Context, c *core.Client, req payment.Create
return
&
payment
.
CreatePaymentResponse
{
TradeNo
:
req
.
OrderID
,
PayURL
:
h5URL
},
nil
}
func
buildWxpayH5Info
(
config
map
[
string
]
string
)
*
h5
.
H5Info
{
tp
:=
wxpayH5Type
info
:=
&
h5
.
H5Info
{
Type
:
&
tp
}
if
appName
:=
strings
.
TrimSpace
(
config
[
"h5AppName"
]);
appName
!=
""
{
info
.
AppName
=
core
.
String
(
appName
)
}
if
appURL
:=
strings
.
TrimSpace
(
config
[
"h5AppUrl"
]);
appURL
!=
""
{
info
.
AppUrl
=
core
.
String
(
appURL
)
}
return
info
}
func
resolveWxpayCreateMode
(
req
payment
.
CreatePaymentRequest
)
(
string
,
error
)
{
if
strings
.
TrimSpace
(
req
.
OpenID
)
!=
""
{
return
wxpayModeJSAPI
,
nil
...
...
backend/internal/payment/provider/wxpay_test.go
View file @
07f23aaa
...
...
@@ -487,3 +487,86 @@ func TestCreatePaymentWithOpenIDReturnsJSAPIResult(t *testing.T) {
t
.
Fatalf
(
"jsapi paySign = %q, want %q"
,
resp
.
JSAPI
.
PaySign
,
"signed-payload"
)
}
}
func
TestCreatePaymentMobileH5IncludesConfiguredSceneInfo
(
t
*
testing
.
T
)
{
origJSAPIPrepay
:=
wxpayJSAPIPrepayWithRequestPayment
origNativePrepay
:=
wxpayNativePrepay
origH5Prepay
:=
wxpayH5Prepay
t
.
Cleanup
(
func
()
{
wxpayJSAPIPrepayWithRequestPayment
=
origJSAPIPrepay
wxpayNativePrepay
=
origNativePrepay
wxpayH5Prepay
=
origH5Prepay
})
jsapiCalls
:=
0
nativeCalls
:=
0
h5Calls
:=
0
wxpayJSAPIPrepayWithRequestPayment
=
func
(
ctx
context
.
Context
,
svc
jsapi
.
JsapiApiService
,
req
jsapi
.
PrepayRequest
)
(
*
jsapi
.
PrepayWithRequestPaymentResponse
,
*
core
.
APIResult
,
error
)
{
jsapiCalls
++
return
&
jsapi
.
PrepayWithRequestPaymentResponse
{},
nil
,
nil
}
wxpayNativePrepay
=
func
(
ctx
context
.
Context
,
svc
native
.
NativeApiService
,
req
native
.
PrepayRequest
)
(
*
native
.
PrepayResponse
,
*
core
.
APIResult
,
error
)
{
nativeCalls
++
return
&
native
.
PrepayResponse
{},
nil
,
nil
}
wxpayH5Prepay
=
func
(
ctx
context
.
Context
,
svc
h5
.
H5ApiService
,
req
h5
.
PrepayRequest
)
(
*
h5
.
PrepayResponse
,
*
core
.
APIResult
,
error
)
{
h5Calls
++
if
req
.
SceneInfo
==
nil
{
t
.
Fatal
(
"expected scene_info, got nil"
)
}
if
got
:=
wxSV
(
req
.
SceneInfo
.
PayerClientIp
);
got
!=
"203.0.113.10"
{
t
.
Fatalf
(
"scene_info payer_client_ip = %q, want %q"
,
got
,
"203.0.113.10"
)
}
if
req
.
SceneInfo
.
H5Info
==
nil
{
t
.
Fatal
(
"expected scene_info.h5_info, got nil"
)
}
if
got
:=
wxSV
(
req
.
SceneInfo
.
H5Info
.
Type
);
got
!=
wxpayH5Type
{
t
.
Fatalf
(
"scene_info.h5_info.type = %q, want %q"
,
got
,
wxpayH5Type
)
}
if
got
:=
wxSV
(
req
.
SceneInfo
.
H5Info
.
AppName
);
got
!=
"Sub2API"
{
t
.
Fatalf
(
"scene_info.h5_info.app_name = %q, want %q"
,
got
,
"Sub2API"
)
}
if
got
:=
wxSV
(
req
.
SceneInfo
.
H5Info
.
AppUrl
);
got
!=
"https://app.example.com"
{
t
.
Fatalf
(
"scene_info.h5_info.app_url = %q, want %q"
,
got
,
"https://app.example.com"
)
}
return
&
h5
.
PrepayResponse
{
H5Url
:
core
.
String
(
"https://wx.tenpay.example/h5pay?prepay_id=1"
),
},
nil
,
nil
}
provider
:=
&
Wxpay
{
config
:
map
[
string
]
string
{
"appId"
:
"wx123"
,
"mchId"
:
"mch123"
,
"h5AppName"
:
"Sub2API"
,
"h5AppUrl"
:
"https://app.example.com"
,
},
coreClient
:
&
core
.
Client
{},
}
resp
,
err
:=
provider
.
CreatePayment
(
context
.
Background
(),
payment
.
CreatePaymentRequest
{
OrderID
:
"sub2_99"
,
Amount
:
"66.88"
,
PaymentType
:
payment
.
TypeWxpay
,
Subject
:
"Balance Recharge"
,
NotifyURL
:
"https://merchant.example/payment/notify"
,
ReturnURL
:
"https://merchant.example/payment/result?resume_token=resume-99"
,
ClientIP
:
"203.0.113.10"
,
IsMobile
:
true
,
})
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
jsapiCalls
!=
0
{
t
.
Fatalf
(
"jsapi prepay calls = %d, want 0"
,
jsapiCalls
)
}
if
nativeCalls
!=
0
{
t
.
Fatalf
(
"native prepay calls = %d, want 0"
,
nativeCalls
)
}
if
h5Calls
!=
1
{
t
.
Fatalf
(
"h5 prepay calls = %d, want 1"
,
h5Calls
)
}
if
!
strings
.
Contains
(
resp
.
PayURL
,
"redirect_url="
)
{
t
.
Fatalf
(
"pay_url = %q, want redirect_url query appended"
,
resp
.
PayURL
)
}
}
frontend/src/components/payment/__tests__/providerConfig.spec.ts
0 → 100644
View file @
07f23aaa
import
{
describe
,
expect
,
it
}
from
'
vitest
'
import
{
PROVIDER_CONFIG_FIELDS
}
from
'
@/components/payment/providerConfig
'
function
findField
(
key
:
string
)
{
const
fields
=
PROVIDER_CONFIG_FIELDS
.
wxpay
||
[]
return
fields
.
find
(
field
=>
field
.
key
===
key
)
}
describe
(
'
PROVIDER_CONFIG_FIELDS.wxpay
'
,
()
=>
{
it
(
'
keeps admin form validation aligned with backend-required credentials
'
,
()
=>
{
expect
(
findField
(
'
publicKeyId
'
)?.
optional
).
toBeFalsy
()
expect
(
findField
(
'
certSerial
'
)?.
optional
).
toBeFalsy
()
})
it
(
'
exposes optional mp and H5 metadata fields for WeChat-specific flows
'
,
()
=>
{
expect
(
findField
(
'
mpAppId
'
)?.
optional
).
toBe
(
true
)
expect
(
findField
(
'
h5AppName
'
)?.
optional
).
toBe
(
true
)
expect
(
findField
(
'
h5AppUrl
'
)?.
optional
).
toBe
(
true
)
})
})
frontend/src/components/payment/providerConfig.ts
View file @
07f23aaa
...
...
@@ -83,12 +83,15 @@ export const PROVIDER_CONFIG_FIELDS: Record<string, ConfigFieldDef[]> = {
],
wxpay
:
[
{
key
:
'
appId
'
,
label
:
'
App ID
'
,
sensitive
:
false
},
{
key
:
'
mpAppId
'
,
label
:
''
,
sensitive
:
false
,
optional
:
true
},
{
key
:
'
mchId
'
,
label
:
''
,
sensitive
:
false
},
{
key
:
'
privateKey
'
,
label
:
''
,
sensitive
:
true
},
{
key
:
'
apiV3Key
'
,
label
:
''
,
sensitive
:
true
},
{
key
:
'
publicKey
'
,
label
:
''
,
sensitive
:
true
},
{
key
:
'
publicKeyId
'
,
label
:
''
,
sensitive
:
false
,
optional
:
true
},
{
key
:
'
certSerial
'
,
label
:
''
,
sensitive
:
false
,
optional
:
true
},
{
key
:
'
publicKeyId
'
,
label
:
''
,
sensitive
:
false
},
{
key
:
'
certSerial
'
,
label
:
''
,
sensitive
:
false
},
{
key
:
'
h5AppName
'
,
label
:
''
,
sensitive
:
false
,
optional
:
true
},
{
key
:
'
h5AppUrl
'
,
label
:
''
,
sensitive
:
false
,
optional
:
true
},
],
stripe
:
[
{
key
:
'
secretKey
'
,
label
:
''
,
sensitive
:
true
},
...
...
frontend/src/i18n/locales/en.ts
View file @
07f23aaa
...
...
@@ -4655,10 +4655,13 @@ export default {
callbackBaseUrl
:
'
Callback Base URL
'
,
field_privateKey
:
'
Private Key
'
,
field_publicKey
:
'
Public Key
'
,
field_mpAppId
:
'
MP App ID
'
,
field_mchId
:
'
Merchant ID
'
,
field_apiV3Key
:
'
API v3 Key
'
,
field_publicKeyId
:
'
Public Key ID
'
,
field_certSerial
:
'
Certificate Serial
'
,
field_h5AppName
:
'
H5 App Name
'
,
field_h5AppUrl
:
'
H5 App URL
'
,
field_secretKey
:
'
Secret Key
'
,
field_publishableKey
:
'
Publishable Key
'
,
field_webhookSecret
:
'
Webhook Secret
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
07f23aaa
...
...
@@ -4819,10 +4819,13 @@ export default {
callbackBaseUrl
:
'
回调基础地址
'
,
field_privateKey
:
'
私钥
'
,
field_publicKey
:
'
公钥
'
,
field_mpAppId
:
'
公众号 App ID
'
,
field_mchId
:
'
商户号
'
,
field_apiV3Key
:
'
API v3 密钥
'
,
field_publicKeyId
:
'
公钥 ID
'
,
field_certSerial
:
'
证书序列号
'
,
field_h5AppName
:
'
H5 应用名称
'
,
field_h5AppUrl
:
'
H5 应用地址
'
,
field_secretKey
:
'
密钥
'
,
field_publishableKey
:
'
公开密钥
'
,
field_webhookSecret
:
'
Webhook 密钥
'
,
...
...
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