feat: 微信支付v3真实下单实现(商户证书就绪即通)+缺证书时明确503提示;certs目录说明

This commit is contained in:
2026-09-13 12:31:44 +08:00
parent 15ab3f0b63
commit 6a971d3061
3 changed files with 662 additions and 615 deletions
+1
View File
@@ -28,3 +28,4 @@ client/src/.share-done
admin/node_modules/ admin/node_modules/
admin/dist/ admin/dist/
server/data/ server/data/
!server/data/certs/README.md
+54 -8
View File
@@ -30,6 +30,25 @@ class WeChatService {
this.tokenExpireTime = 0 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 * 获取 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 { try {
// 注意:实际生产环境需要处理网络请求和签名 // 1. 统一下单(真·微信支付 v3 JSAPI)
// const response = await axios.post(fullUrl, orderParams, { ... }) const bodyStr = JSON.stringify(orderParams)
// const prepayId = response.data.prepay_id 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 = response.data && response.data.prepay_id
const prepayId = `wx_mock_${Date.now()}` 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 paySignStr = `${this.appId}\n${timestamp}\n${nonceStr}\nprepay_id=${prepayId}\n`
const paySign = crypto.createSign('RSA-SHA256') const paySign = crypto.createSign('RSA-SHA256')
paySign.update(paySignStr) paySign.update(paySignStr)
@@ -179,8 +223,10 @@ class WeChatService {
orderNo orderNo
} }
} catch (error) { } catch (error) {
console.error('微信支付下单失败:', error) console.error('微信支付下单失败:', error.response ? error.response.data : error.message)
throw error if (error.code === 'PAY_NOT_CONFIGURED') throw error
const wxMsg = error.response && error.response.data && error.response.data.message
throw new Error(wxMsg ? `微信支付下单失败:${wxMsg}` : '微信支付下单失败,请稍后再试')
} }
} }