const axios = require('axios') const config = require('../config.json') function getPaymentConfig() { return config.pay || {} } function normalizePayBaseUrl(url) { return String(url || '').trim().replace(/\/+$/, '') } function buildPaymentApiUrl(act, params = {}) { const paymentConfig = getPaymentConfig() const baseUrl = normalizePayBaseUrl(paymentConfig.url) if (!baseUrl) { throw new Error('支付配置错误') } const url = new URL(`${baseUrl}/api.php`) url.searchParams.set('act', act) for (const [key, value] of Object.entries(params)) { if (value === undefined || value === null || value === '') continue url.searchParams.set(key, String(value)) } return url.toString() } function parsePaymentResponseBody(data) { if (data && typeof data === 'object') return data if (typeof data !== 'string') return null const text = data.trim() if (!text) return null try { return JSON.parse(text) } catch (_) { return null } } function getAxiosPaymentOptions(extra = {}) { return { timeout: 15000, maxRedirects: 5, proxy: false, validateStatus: (status) => status >= 200 && status < 500, ...extra } } function formatPaymentHttpError(error, fallbackMessage) { if (error?.code === 'ECONNABORTED') { return '支付平台响应超时,请稍后重试' } if (error?.code === 'ERR_FR_TOO_MANY_REDIRECTS') { return '支付平台重定向异常,请检查 pay.url 配置或关闭系统代理后重试' } const status = Number(error?.response?.status || 0) const parsed = parsePaymentResponseBody(error?.response?.data) if (parsed?.msg) return String(parsed.msg) if (status === 503) return '支付平台暂时不可用,请稍后重试' if (status >= 500) return `支付平台异常(${status}),请稍后重试` return fallbackMessage || error?.message || '支付平台请求失败' } async function queryPaymentOrder(orderId, logger) { const paymentConfig = getPaymentConfig() if (!paymentConfig.pid || !paymentConfig.url || !paymentConfig.key) { throw new Error('支付配置错误') } const queryUrl = buildPaymentApiUrl('order', { pid: paymentConfig.pid, key: paymentConfig.key, out_trade_no: orderId }) let response try { response = await axios.get(queryUrl, getAxiosPaymentOptions()) } catch (error) { const message = formatPaymentHttpError(error, '查询支付状态失败') logger?.warn?.(`查询支付状态失败,订单号:${orderId},URL:${queryUrl},原因:${message}`) throw new Error(message) } const result = parsePaymentResponseBody(response.data) if (!result) { logger?.warn?.(`支付平台返回非 JSON,订单号:${orderId},HTTP ${response.status}`) throw new Error('支付平台响应异常') } return result } module.exports = { normalizePayBaseUrl, buildPaymentApiUrl, getAxiosPaymentOptions, formatPaymentHttpError, parsePaymentResponseBody, queryPaymentOrder }