更新项目配置,调整 .next 目录的清理脚本以支持多个子目录的删除,优化提现功能的环境变量配置,确保与现有支付配置一致。同时,增强 API 日志记录以便于调试,更新文档以反映新的环境变量使用方式,提升系统的可维护性和用户体验。

This commit is contained in:
2026-02-07 15:30:31 +08:00
parent 8e67eb5d62
commit de10a203b3
8 changed files with 130 additions and 32 deletions

View File

@@ -18,13 +18,34 @@ export interface WechatTransferConfig {
certSerialNo: string
}
// 与小程序支付、lib/payment/config 保持一致,复用同一套 env
const DEFAULT_MCH_ID = '1318592501'
const DEFAULT_APP_ID = 'wxb8bbb2b10dec74aa'
/** 从 apiclient_cert.pem 读取证书序列号(与 lib/payment 的 WECHAT_CERT_PATH 复用) */
function getCertSerialNoFromPath(certPath: string): string {
const p = path.isAbsolute(certPath) ? certPath : path.join(process.cwd(), certPath)
const pem = fs.readFileSync(p, 'utf8')
const cert = new crypto.X509Certificate(pem)
const raw = (cert.serialNumber || '').replace(/:/g, '').replace(/^0+/, '') || ''
return raw.toUpperCase()
}
function getConfig(): WechatTransferConfig {
const mchId = process.env.WECHAT_MCH_ID || process.env.WECHAT_MCHID || ''
const appId = process.env.WECHAT_APP_ID || process.env.WECHAT_APPID || 'wxb8bbb2b10dec74aa'
const mchId = process.env.WECHAT_MCH_ID || process.env.WECHAT_MCHID || DEFAULT_MCH_ID
const appId = process.env.WECHAT_APP_ID || process.env.WECHAT_APPID || DEFAULT_APP_ID
const apiV3Key = process.env.WECHAT_API_V3_KEY || process.env.WECHAT_MCH_KEY || ''
const keyPath = process.env.WECHAT_KEY_PATH || process.env.WECHAT_MCH_PRIVATE_KEY_PATH || ''
const keyContent = process.env.WECHAT_MCH_PRIVATE_KEY || ''
const certSerialNo = process.env.WECHAT_MCH_CERT_SERIAL_NO || ''
let certSerialNo = process.env.WECHAT_MCH_CERT_SERIAL_NO || ''
const certPath = process.env.WECHAT_CERT_PATH || ''
if (!certSerialNo && certPath) {
try {
certSerialNo = getCertSerialNoFromPath(certPath)
} catch (e) {
console.warn('[wechat-transfer] 从证书文件读取序列号失败:', (e as Error).message)
}
}
return {
mchId,
appId,
@@ -193,8 +214,24 @@ export async function createTransferUserConfirm(
params: CreateTransferUserConfirmParams
): Promise<CreateTransferUserConfirmResult> {
const cfg = getConfig()
if (!cfg.mchId || !cfg.appId || !cfg.certSerialNo) {
return { success: false, errorCode: 'CONFIG_ERROR', errorMessage: '微信转账配置不完整' }
if (!cfg.mchId || !cfg.appId) {
return { success: false, errorCode: 'CONFIG_ERROR', errorMessage: '微信转账配置不完整:缺少商户号或 AppID' }
}
if (!cfg.certSerialNo) {
const certPath = process.env.WECHAT_CERT_PATH || ''
const hint = certPath
? `已配置 WECHAT_CERT_PATH=${certPath} 但读取序列号失败,请检查文件存在且为有效 PEM。或直接在 .env 配置 WECHAT_MCH_CERT_SERIAL_NOopenssl x509 -in apiclient_cert.pem -noout -serial 获取)`
: '请在 .env 中配置 WECHAT_CERT_PATHapiclient_cert.pem 路径),或配置 WECHAT_MCH_CERT_SERIAL_NO证书序列号'
return { success: false, errorCode: 'CONFIG_ERROR', errorMessage: `微信转账配置不完整:缺少证书序列号。${hint}` }
}
try {
getPrivateKey()
} catch (e) {
return {
success: false,
errorCode: 'CONFIG_ERROR',
errorMessage: `微信转账配置不完整:商户私钥未配置。请在 .env 中配置 WECHAT_KEY_PATHapiclient_key.pem 路径)或 WECHAT_MCH_PRIVATE_KEY`,
}
}
const urlPath = '/v3/fund-app/mch-transfer/transfer-bills'
@@ -218,6 +255,14 @@ export async function createTransferUserConfirm(
const signature = buildSignature('POST', urlPath, timestamp, nonce, bodyStr)
const authorization = buildAuthorization(timestamp, nonce, signature)
// 发起请求前打印:便于区分是请求微信缺参,还是管理端审核传参问题
console.log('[wechat-transfer] ========== 请求微信支付(用户确认模式)==========')
console.log('[wechat-transfer] URL:', `${BASE_URL}${urlPath}`)
console.log('[wechat-transfer] 请求体 body:', JSON.stringify(body, null, 2))
console.log('[wechat-transfer] 当前配置: mchId=', cfg.mchId, 'appId=', cfg.appId, 'certSerialNo=', cfg.certSerialNo ? `${cfg.certSerialNo.slice(0, 8)}...` : '(空)')
console.log('[wechat-transfer] notify_url:', body.notify_url)
console.log('[wechat-transfer] ========================================')
const res = await fetch(`${BASE_URL}${urlPath}`, {
method: 'POST',
headers: {
@@ -229,6 +274,7 @@ export async function createTransferUserConfirm(
body: bodyStr,
})
const data = (await res.json()) as Record<string, unknown>
console.log('[wechat-transfer] 微信响应 status=', res.status, 'body=', JSON.stringify(data, null, 2))
if (res.ok && res.status >= 200 && res.status < 300) {
const state = (data.state as string) || ''
return {