feat: 微信支付v3真实下单实现(商户证书就绪即通)+缺证书时明确503提示;certs目录说明
This commit is contained in:
@@ -28,3 +28,4 @@ client/src/.share-done
|
||||
admin/node_modules/
|
||||
admin/dist/
|
||||
server/data/
|
||||
!server/data/certs/README.md
|
||||
|
||||
@@ -30,6 +30,25 @@ class WeChatService {
|
||||
this.tokenExpireTime = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信支付 v3 是否配置完整(商户号+序列号+商户私钥)
|
||||
*/
|
||||
isPayConfigured() {
|
||||
return !!(this.mchId && this.serialNo && this.privateKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成微信支付 v3 请求头 Authorization(商户私钥签名)
|
||||
*/
|
||||
buildV3Auth(method, urlPath, timestamp, nonce, bodyStr) {
|
||||
const message = `${method}\n${urlPath}\n${timestamp}\n${nonce}\n${bodyStr}\n`
|
||||
const signer = crypto.createSign('RSA-SHA256')
|
||||
signer.update(message)
|
||||
signer.end()
|
||||
const signature = signer.sign(this.privateKey, 'base64')
|
||||
return `WECHATPAY2-SHA256-RFC4648 mchid="${this.mchId}",nonce_str="${nonce}",signature="${signature}",timestamp="${timestamp}",serial_no="${this.serialNo}"`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Access Token
|
||||
*/
|
||||
@@ -154,15 +173,40 @@ class WeChatService {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isPayConfigured()) {
|
||||
const err = new Error('支付功能暂未开通:缺少商户证书配置,请联系管理员在服务器放置 apiclient_key.pem 后重启服务')
|
||||
err.code = 'PAY_NOT_CONFIGURED'
|
||||
throw err
|
||||
}
|
||||
|
||||
try {
|
||||
// 注意:实际生产环境需要处理网络请求和签名
|
||||
// const response = await axios.post(fullUrl, orderParams, { ... })
|
||||
// const prepayId = response.data.prepay_id
|
||||
// 1. 统一下单(真·微信支付 v3 JSAPI)
|
||||
const bodyStr = JSON.stringify(orderParams)
|
||||
const auth = this.buildV3Auth('POST', url, timestamp, nonceStr, bodyStr)
|
||||
const controller = new AbortController()
|
||||
const timer = setTimeout(() => controller.abort(), 15000)
|
||||
let response
|
||||
try {
|
||||
response = await axios.post(fullUrl, bodyStr, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Authorization: auth,
|
||||
'User-Agent': 'yuzhiran-ai-dimension/1.2'
|
||||
},
|
||||
timeout: 15000,
|
||||
signal: controller.signal
|
||||
})
|
||||
} finally {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
|
||||
// 模拟获取 prepay_id
|
||||
const prepayId = `wx_mock_${Date.now()}`
|
||||
const prepayId = response.data && response.data.prepay_id
|
||||
if (!prepayId) {
|
||||
throw new Error('微信支付下单未返回 prepay_id')
|
||||
}
|
||||
|
||||
// 2. 生成前端调起支付的签名
|
||||
// 2. 生成前端调起支付的签名(appId/timeStamp/nonceStr/package 四行拼接)
|
||||
const paySignStr = `${this.appId}\n${timestamp}\n${nonceStr}\nprepay_id=${prepayId}\n`
|
||||
const paySign = crypto.createSign('RSA-SHA256')
|
||||
paySign.update(paySignStr)
|
||||
@@ -179,8 +223,10 @@ class WeChatService {
|
||||
orderNo
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('微信支付下单失败:', error)
|
||||
throw error
|
||||
console.error('微信支付下单失败:', error.response ? error.response.data : error.message)
|
||||
if (error.code === 'PAY_NOT_CONFIGURED') throw error
|
||||
const wxMsg = error.response && error.response.data && error.response.data.message
|
||||
throw new Error(wxMsg ? `微信支付下单失败:${wxMsg}` : '微信支付下单失败,请稍后再试')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user