| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- const axios = require('axios')
- const config = require('../config.json')
- const db = require('../plugin/DataBase/db')
- const { insertLedgerRecord } = require('./Lepao/CountLedger')
- const REFUND_WINDOW_MS = 7 * 24 * 60 * 60 * 1000
- const ORDER_STATE_COMPLETED = 2
- const ORDER_STATE_REFUNDED = 5
- function evaluateRefundEligibility({
- state,
- payTime,
- userLepaoCount,
- goodsLepaoCount,
- skipTimeLimit = false
- }) {
- if (Number(state) === ORDER_STATE_REFUNDED) {
- return { canRefund: false, reason: '订单已退款' }
- }
- if (Number(state) !== ORDER_STATE_COMPLETED) {
- return { canRefund: false, reason: '仅已完成订单可申请退款' }
- }
- if (!payTime) {
- return { canRefund: false, reason: '订单支付时间异常' }
- }
- if (!skipTimeLimit && Date.now() - Number(payTime) > REFUND_WINDOW_MS) {
- return { canRefund: false, reason: '已超过7天退款期限' }
- }
- const purchasedCount = Number(goodsLepaoCount || 0)
- const remainingCount = Number(userLepaoCount || 0)
- if (purchasedCount > 0 && remainingCount <= purchasedCount) {
- return { canRefund: false, reason: '账户剩余次数需大于订单购买次数才可退款' }
- }
- return { canRefund: true, reason: '' }
- }
- 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 formatPaymentRefundError(response, fallbackMessage) {
- const status = Number(response?.status || 0)
- const parsed = parsePaymentResponseBody(response?.data)
- if (parsed?.msg) return String(parsed.msg)
- if (status === 503) return '支付平台暂时不可用,请稍后重试'
- if (status >= 500) return `支付平台异常(${status}),请稍后重试`
- if (status >= 400) return `支付平台拒绝退款(${status})`
- return fallbackMessage || '支付平台退款失败'
- }
- async function requestPaymentRefund({ orderId, tradeNo, money, logger }) {
- const paymentConfig = config.pay || {}
- if (!paymentConfig.url || !paymentConfig.pid || !paymentConfig.key) {
- throw new Error('支付配置错误')
- }
- const params = new URLSearchParams()
- params.append('pid', String(paymentConfig.pid))
- params.append('key', paymentConfig.key)
- if (tradeNo) {
- params.append('trade_no', tradeNo)
- } else {
- params.append('out_trade_no', orderId)
- }
- params.append('money', String(money))
- const refundUrl = `${paymentConfig.url}/api.php?act=refund`
- let response
- try {
- response = await axios.post(refundUrl, params.toString(), {
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
- timeout: 30000,
- validateStatus: () => true
- })
- } catch (error) {
- logger?.error?.(`易支付退款请求失败 订单号:${orderId}:${error.stack || error}`)
- if (error.code === 'ECONNABORTED') {
- throw new Error('支付平台响应超时,请稍后重试')
- }
- throw new Error('无法连接支付平台,请稍后重试')
- }
- const result = parsePaymentResponseBody(response.data)
- logger?.info?.(`易支付退款响应 订单号:${orderId},HTTP ${response.status},结果:${JSON.stringify(result ?? response.data)}`)
- if (response.status >= 400) {
- throw new Error(formatPaymentRefundError(response, '支付平台退款失败'))
- }
- if (!result || Number(result.code) !== 1) {
- throw new Error(result?.msg || '支付平台退款失败')
- }
- return result
- }
- async function loadRefundContext(orderId) {
- const rows = await db.query(
- `SELECT
- o.orderId,
- o.state,
- o.price,
- o.pay_id,
- o.pay_time,
- o.create_user,
- g.lepao_count,
- g.ic_count
- FROM orders o
- LEFT JOIN goods g ON o.goods_id = g.id
- WHERE o.orderId = ?
- LIMIT 1`,
- [orderId]
- )
- if (!rows || rows.length !== 1) {
- return null
- }
- const order = rows[0]
- const userRows = await db.query(
- 'SELECT lepao_count, ic_count FROM users WHERE uuid = ? LIMIT 1',
- [order.create_user]
- )
- if (!userRows || userRows.length !== 1) {
- return null
- }
- return { order, user: userRows[0] }
- }
- async function executeOrderRefund({
- orderId,
- operatorUuid = null,
- skipTimeLimit = false,
- logger
- }) {
- try {
- const context = await loadRefundContext(orderId)
- if (!context) {
- return { ok: false, msg: '订单或用户不存在' }
- }
- const { order, user } = context
- const eligibility = evaluateRefundEligibility({
- state: order.state,
- payTime: order.pay_time,
- userLepaoCount: user.lepao_count,
- goodsLepaoCount: order.lepao_count,
- skipTimeLimit
- })
- if (!eligibility.canRefund) {
- return { ok: false, msg: eligibility.reason }
- }
- const deductLepao = Number(order.lepao_count || 0)
- const deductIc = Number(order.ic_count || 0)
- if (deductLepao > 0 && Number(user.lepao_count || 0) < deductLepao) {
- return { ok: false, msg: '账户乐跑次数不足,无法完成退款' }
- }
- await requestPaymentRefund({
- orderId: order.orderId,
- tradeNo: order.pay_id,
- money: order.price,
- logger
- })
- const conn = await db.connect()
- try {
- await conn.beginTransaction()
- const [orderRows] = await conn.execute(
- `SELECT
- o.orderId,
- o.state,
- o.create_user,
- g.lepao_count,
- g.ic_count
- FROM orders o
- LEFT JOIN goods g ON o.goods_id = g.id
- WHERE o.orderId = ?
- FOR UPDATE`,
- [orderId]
- )
- if (!orderRows || orderRows.length !== 1) {
- await conn.rollback()
- return { ok: false, msg: '订单不存在' }
- }
- const lockedOrder = orderRows[0]
- if (Number(lockedOrder.state) !== ORDER_STATE_COMPLETED) {
- await conn.rollback()
- return { ok: false, msg: '订单状态已变更,请刷新后重试' }
- }
- const [userRows] = await conn.execute(
- 'SELECT lepao_count, ic_count FROM users WHERE uuid = ? FOR UPDATE',
- [lockedOrder.create_user]
- )
- if (!userRows || userRows.length !== 1) {
- await conn.rollback()
- return { ok: false, msg: '用户不存在' }
- }
- const lockedUser = userRows[0]
- const lockedDeductLepao = Number(lockedOrder.lepao_count || 0)
- const lockedDeductIc = Number(lockedOrder.ic_count || 0)
- const beforeLepao = Number(lockedUser.lepao_count || 0)
- const beforeIc = Number(lockedUser.ic_count || 0)
- if (lockedDeductLepao > 0 && beforeLepao < lockedDeductLepao) {
- await conn.rollback()
- logger?.error?.(`退款支付已成功但扣次失败,需人工处理,订单号:${orderId}`)
- return { ok: false, msg: '支付已退款但扣减次数失败,请联系客服处理' }
- }
- const afterLepao = beforeLepao - lockedDeductLepao
- const afterIc = Math.max(0, beforeIc - lockedDeductIc)
- const [updateUserRes] = await conn.execute(
- 'UPDATE users SET lepao_count = ?, ic_count = ? WHERE uuid = ?',
- [afterLepao, afterIc, lockedOrder.create_user]
- )
- if (!updateUserRes || updateUserRes.affectedRows !== 1) {
- await conn.rollback()
- logger?.error?.(`退款支付已成功但更新用户失败,需人工处理,订单号:${orderId}`)
- return { ok: false, msg: '支付已退款但更新账户失败,请联系客服处理' }
- }
- const [updateOrderRes] = await conn.execute(
- 'UPDATE orders SET state = ? WHERE orderId = ? AND state = ?',
- [ORDER_STATE_REFUNDED, orderId, ORDER_STATE_COMPLETED]
- )
- if (!updateOrderRes || updateOrderRes.affectedRows !== 1) {
- await conn.rollback()
- logger?.error?.(`退款支付已成功但更新订单失败,需人工处理,订单号:${orderId}`)
- return { ok: false, msg: '支付已退款但更新订单失败,请联系客服处理' }
- }
- if (lockedDeductLepao !== 0) {
- await insertLedgerRecord({
- executor: conn,
- userUuid: lockedOrder.create_user,
- delta: -lockedDeductLepao,
- balanceBefore: beforeLepao,
- balanceAfter: afterLepao,
- bizType: 'purchase_refund',
- bizId: orderId,
- operatorUuid,
- remark: `订单退款:${orderId}`
- })
- }
- await conn.commit()
- return { ok: true, msg: '退款成功' }
- } catch (dbError) {
- try { await conn.rollback() } catch (_) { }
- logger?.error?.(`退款入账失败 ${orderId}: ${dbError.stack || dbError}`)
- return { ok: false, msg: '支付已退款但入账失败,请联系客服处理' }
- }
- } catch (error) {
- logger?.error?.(`订单退款失败 ${orderId}: ${error.stack || error}`)
- return { ok: false, msg: error.message || '退款失败,请稍后再试' }
- }
- }
- module.exports = {
- REFUND_WINDOW_MS,
- ORDER_STATE_REFUNDED,
- evaluateRefundEligibility,
- executeOrderRefund
- }
|