230 lines
6.0 KiB
TypeScript
230 lines
6.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
||
import { db } from '@/lib/db'
|
||
import crypto from 'crypto'
|
||
|
||
// 验证易支付回调签名
|
||
function verifySign(params: Record<string, any>, key: string): boolean {
|
||
const sign = params.sign
|
||
if (!sign) return false
|
||
|
||
// 移除sign和sign_type参数
|
||
const filtered: Record<string, any> = {}
|
||
Object.keys(params)
|
||
.sort()
|
||
.forEach((k) => {
|
||
if (k !== 'sign' && k !== 'sign_type' && params[k] !== '' && params[k] !== null && params[k] !== undefined) {
|
||
filtered[k] = params[k]
|
||
}
|
||
})
|
||
|
||
// 拼接字符串
|
||
const signStr = Object.keys(filtered)
|
||
.map((k) => `${k}=${filtered[k]}`)
|
||
.join('&')
|
||
const signStrWithKey = `${signStr}&key=${key}`
|
||
|
||
// MD5加密并转大写
|
||
const calculatedSign = crypto.createHash('md5').update(signStrWithKey).digest('hex').toUpperCase()
|
||
|
||
return calculatedSign === sign.toUpperCase()
|
||
}
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const formData = await request.formData()
|
||
const params: Record<string, any> = {}
|
||
|
||
// 将FormData转换为对象
|
||
formData.forEach((value, key) => {
|
||
params[key] = value.toString()
|
||
})
|
||
|
||
// 也支持JSON格式
|
||
if (Object.keys(params).length === 0) {
|
||
try {
|
||
const json = await request.json()
|
||
Object.assign(params, json)
|
||
} catch {
|
||
// 忽略JSON解析错误
|
||
}
|
||
}
|
||
|
||
console.log('📥 收到支付回调:', params)
|
||
|
||
const epayKey = process.env.EPAY_KEY
|
||
if (!epayKey) {
|
||
console.error('❌ 易支付密钥未配置')
|
||
return NextResponse.text('FAIL', { status: 500 })
|
||
}
|
||
|
||
// 验证签名
|
||
if (!verifySign(params, epayKey)) {
|
||
console.error('❌ 签名验证失败:', params)
|
||
return NextResponse.text('FAIL', { status: 400 })
|
||
}
|
||
|
||
// 获取订单信息
|
||
const orderNo = params.out_trade_no
|
||
const tradeNo = params.trade_no
|
||
const tradeStatus = params.trade_status
|
||
const paymentMethod = params.type || params.payment_method
|
||
|
||
if (!orderNo) {
|
||
console.error('❌ 订单号缺失')
|
||
return NextResponse.text('FAIL', { status: 400 })
|
||
}
|
||
|
||
// 查找订单
|
||
const order = await db.findOrderByOrderNo(orderNo)
|
||
if (!order) {
|
||
console.error('❌ 订单不存在:', orderNo)
|
||
return NextResponse.text('FAIL', { status: 404 })
|
||
}
|
||
|
||
// 检查订单状态
|
||
if (order.status === 'paid') {
|
||
console.log('✅ 订单已支付,跳过处理:', orderNo)
|
||
return NextResponse.text('SUCCESS')
|
||
}
|
||
|
||
// 验证支付状态
|
||
// 易支付通常 trade_status = 'TRADE_SUCCESS' 表示支付成功
|
||
if (tradeStatus === 'TRADE_SUCCESS' || params.status === 'success' || params.code === '1') {
|
||
// 更新订单状态
|
||
await db.updateOrderStatus(
|
||
orderNo,
|
||
'paid',
|
||
tradeNo,
|
||
paymentMethod,
|
||
JSON.stringify(params)
|
||
)
|
||
|
||
console.log('✅ 订单支付成功:', {
|
||
orderNo,
|
||
tradeNo,
|
||
amount: order.amount,
|
||
paymentMethod,
|
||
})
|
||
|
||
// TODO: 这里可以添加其他业务逻辑,比如:
|
||
// - 发送邮件通知
|
||
// - 更新用户权限
|
||
// - 记录日志等
|
||
|
||
return NextResponse.text('SUCCESS')
|
||
} else {
|
||
// 支付失败
|
||
await db.updateOrderStatus(
|
||
orderNo,
|
||
'failed',
|
||
tradeNo,
|
||
paymentMethod,
|
||
JSON.stringify(params)
|
||
)
|
||
|
||
console.log('❌ 订单支付失败:', {
|
||
orderNo,
|
||
tradeStatus,
|
||
params,
|
||
})
|
||
|
||
return NextResponse.text('SUCCESS') // 即使失败也要返回SUCCESS,避免重复通知
|
||
}
|
||
} catch (error) {
|
||
console.error('处理支付回调错误:', error)
|
||
return NextResponse.text('FAIL', { status: 500 })
|
||
}
|
||
}
|
||
|
||
// 也支持GET请求(某些支付平台可能使用GET)
|
||
export async function GET(request: NextRequest) {
|
||
try {
|
||
const searchParams = request.nextUrl.searchParams
|
||
const params: Record<string, any> = {}
|
||
|
||
searchParams.forEach((value, key) => {
|
||
params[key] = value
|
||
})
|
||
|
||
console.log('📥 收到支付回调(GET):', params)
|
||
|
||
const epayKey = process.env.EPAY_KEY
|
||
if (!epayKey) {
|
||
console.error('❌ 易支付密钥未配置')
|
||
return NextResponse.text('FAIL', { status: 500 })
|
||
}
|
||
|
||
// 验证签名
|
||
if (!verifySign(params, epayKey)) {
|
||
console.error('❌ 签名验证失败:', params)
|
||
return NextResponse.text('FAIL', { status: 400 })
|
||
}
|
||
|
||
// 获取订单信息
|
||
const orderNo = params.out_trade_no
|
||
const tradeNo = params.trade_no
|
||
const tradeStatus = params.trade_status
|
||
const paymentMethod = params.type || params.payment_method
|
||
|
||
if (!orderNo) {
|
||
console.error('❌ 订单号缺失')
|
||
return NextResponse.text('FAIL', { status: 400 })
|
||
}
|
||
|
||
// 查找订单
|
||
const order = await db.findOrderByOrderNo(orderNo)
|
||
if (!order) {
|
||
console.error('❌ 订单不存在:', orderNo)
|
||
return NextResponse.text('FAIL', { status: 404 })
|
||
}
|
||
|
||
// 检查订单状态
|
||
if (order.status === 'paid') {
|
||
console.log('✅ 订单已支付,跳过处理:', orderNo)
|
||
return NextResponse.text('SUCCESS')
|
||
}
|
||
|
||
// 验证支付状态
|
||
if (tradeStatus === 'TRADE_SUCCESS' || params.status === 'success' || params.code === '1') {
|
||
// 更新订单状态
|
||
await db.updateOrderStatus(
|
||
orderNo,
|
||
'paid',
|
||
tradeNo,
|
||
paymentMethod,
|
||
JSON.stringify(params)
|
||
)
|
||
|
||
console.log('✅ 订单支付成功:', {
|
||
orderNo,
|
||
tradeNo,
|
||
amount: order.amount,
|
||
paymentMethod,
|
||
})
|
||
|
||
return NextResponse.text('SUCCESS')
|
||
} else {
|
||
// 支付失败
|
||
await db.updateOrderStatus(
|
||
orderNo,
|
||
'failed',
|
||
tradeNo,
|
||
paymentMethod,
|
||
JSON.stringify(params)
|
||
)
|
||
|
||
console.log('❌ 订单支付失败:', {
|
||
orderNo,
|
||
tradeStatus,
|
||
params,
|
||
})
|
||
|
||
return NextResponse.text('SUCCESS')
|
||
}
|
||
} catch (error) {
|
||
console.error('处理支付回调错误:', error)
|
||
return NextResponse.text('FAIL', { status: 500 })
|
||
}
|
||
}
|
||
|