Files
soul-yongping/app/api/withdraw/route.ts

164 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 提现API
* 用户提现到微信零钱或支付宝
*/
import { NextRequest, NextResponse } from 'next/server'
import { query, getConfig } from '@/lib/db'
// 确保提现表存在
async function ensureWithdrawalsTable() {
try {
await query(`
CREATE TABLE IF NOT EXISTS withdrawals (
id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
account_type VARCHAR(20) DEFAULT 'wechat',
account VARCHAR(100),
status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP NULL,
INDEX idx_user_id (user_id),
INDEX idx_status (status)
)
`)
} catch (e) {
console.log('[Withdraw] 表已存在或创建失败')
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { userId, amount } = body
if (!userId) {
return NextResponse.json({ success: false, message: '缺少用户ID' }, { status: 400 })
}
if (!amount || amount <= 0) {
return NextResponse.json({ success: false, message: '提现金额无效' }, { status: 400 })
}
// 读取最低提现门槛
let minWithdrawAmount = 10 // 默认值
try {
const config = await getConfig('referral_config')
if (config?.minWithdrawAmount) {
minWithdrawAmount = Number(config.minWithdrawAmount)
}
} catch (e) {
console.warn('[Withdraw] 读取配置失败,使用默认值 10 元')
}
// 检查最低提现门槛
if (amount < minWithdrawAmount) {
return NextResponse.json({
success: false,
message: `最低提现金额为 ¥${minWithdrawAmount},当前 ¥${amount}`
}, { status: 400 })
}
// 确保表存在
await ensureWithdrawalsTable()
// 查询用户信息
const users = await query('SELECT * FROM users WHERE id = ?', [userId]) as any[]
if (!users || users.length === 0) {
return NextResponse.json({ success: false, message: '用户不存在' }, { status: 404 })
}
const user = users[0]
// 微信零钱提现需要 open_id小程序/公众号登录获得)
const openId = user.open_id || ''
const wechatId = user.wechat || user.wechat_id || ''
const alipayId = user.alipay || ''
if (!openId && !alipayId) {
return NextResponse.json({
success: false,
message: '提现到微信零钱需先使用微信登录;或绑定支付宝后提现到支付宝',
needBind: true
})
}
// 查询可提现金额(待结算收益)
let totalEarnings = 0
try {
const earningsResult = await query(`
SELECT COALESCE(SUM(commission), 0) as total_commission
FROM referral_bindings
WHERE referrer_id = ? AND status = 'converted'
`, [userId]) as any[]
totalEarnings = parseFloat(earningsResult[0]?.total_commission || 0)
} catch (e) {
// 如果表不存在收益为0
console.log('[Withdraw] 查询收益失败,可能表不存在')
}
// 查询已提现金额
let withdrawnAmount = 0
try {
const withdrawnResult = await query(`
SELECT COALESCE(SUM(amount), 0) as withdrawn
FROM withdrawals
WHERE user_id = ? AND status = 'completed'
`, [userId]) as any[]
withdrawnAmount = parseFloat(withdrawnResult[0]?.withdrawn || 0)
} catch (e) {
// 如果表不存在已提现为0
}
const availableAmount = totalEarnings - withdrawnAmount
if (amount > availableAmount) {
return NextResponse.json({
success: false,
message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)}`
})
}
// 创建提现记录(微信零钱需保存 wechat_openid 供后台批准时调用商家转账到零钱)
const withdrawId = `W${Date.now()}`
const accountType = alipayId ? 'alipay' : 'wechat'
const account = alipayId || wechatId
try {
await query(`
INSERT INTO withdrawals (id, user_id, amount, status, wechat_openid, created_at)
VALUES (?, ?, ?, 'pending', ?, NOW())
`, [withdrawId, userId, amount, accountType === 'wechat' ? openId : null])
// 微信零钱由后台批准时调用「商家转账到零钱」;支付宝/无 openid 时仅标记成功(需线下打款)
if (accountType !== 'wechat' || !openId) {
await query(`UPDATE withdrawals SET status = 'success', processed_at = NOW() WHERE id = ?`, [withdrawId])
await query(`
UPDATE users SET withdrawn_earnings = withdrawn_earnings + ?, pending_earnings = GREATEST(0, pending_earnings - ?) WHERE id = ?
`, [amount, amount, userId])
}
} catch (e) {
console.log('[Withdraw] 创建提现记录失败:', e)
}
return NextResponse.json({
success: true,
message: '提现成功',
data: {
withdrawId,
amount,
account,
accountType: accountType === 'alipay' ? '支付宝' : '微信'
}
})
} catch (error) {
console.error('[Withdraw] Error:', error)
return NextResponse.json({
success: false,
message: '提现失败: ' + String(error)
}, { status: 500 })
}
}