157 lines
5.7 KiB
TypeScript
157 lines
5.7 KiB
TypeScript
/**
|
||
* 微信支付回调通知 API
|
||
* 基于 Universal_Payment_Module v4.0 设计
|
||
*
|
||
* POST /api/payment/wechat/notify
|
||
*/
|
||
|
||
import { type NextRequest, NextResponse } from "next/server"
|
||
import { PaymentFactory, SignatureError } from "@/lib/payment"
|
||
import { query } from "@/lib/db"
|
||
|
||
// 确保网关已注册
|
||
import "@/lib/payment/wechat"
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
// 获取XML原始数据
|
||
const xmlData = await request.text()
|
||
|
||
console.log("[Wechat Notify] 收到回调:", xmlData.slice(0, 200))
|
||
|
||
// 创建微信支付网关
|
||
const gateway = PaymentFactory.create("wechat_native")
|
||
|
||
try {
|
||
// 解析并验证回调数据
|
||
const notifyResult = gateway.parseNotify(xmlData)
|
||
|
||
if (notifyResult.status === "paid") {
|
||
console.log("[Wechat Notify] 支付成功:", {
|
||
tradeSn: notifyResult.tradeSn,
|
||
platformSn: notifyResult.platformSn,
|
||
amount: notifyResult.payAmount / 100, // 转换为元
|
||
payTime: notifyResult.payTime,
|
||
})
|
||
|
||
// === ✅ 1. 更新订单状态 ===
|
||
try {
|
||
// 通过 transaction_id 查找订单
|
||
const orderRows = await query(`
|
||
SELECT id, user_id, amount, product_type, product_id
|
||
FROM orders
|
||
WHERE transaction_id = ? AND status = 'created'
|
||
LIMIT 1
|
||
`, [notifyResult.tradeSn]) as any[]
|
||
|
||
if (orderRows.length === 0) {
|
||
console.error('[Wechat Notify] ❌ 订单不存在或已处理:', notifyResult.tradeSn)
|
||
} else {
|
||
const order = orderRows[0]
|
||
const orderId = order.id
|
||
const userId = order.user_id
|
||
const amount = parseFloat(order.amount)
|
||
const productType = order.product_type
|
||
const productId = order.product_id
|
||
|
||
// 更新订单状态为已支付
|
||
await query(`
|
||
UPDATE orders
|
||
SET status = 'paid',
|
||
pay_time = ?,
|
||
updated_at = NOW()
|
||
WHERE id = ?
|
||
`, [notifyResult.payTime, orderId])
|
||
|
||
console.log('[Wechat Notify] ✅ 订单状态已更新:', { orderId, status: 'paid' })
|
||
|
||
// === ✅ 2. 解锁内容/开通权限 ===
|
||
if (productType === 'fullbook') {
|
||
// 购买全书
|
||
await query('UPDATE users SET has_full_book = 1 WHERE id = ?', [userId])
|
||
console.log('[Wechat Notify] ✅ 全书权限已开通:', userId)
|
||
} else if (productType === 'section' && productId) {
|
||
// 购买单个章节(这里需要根据你的业务逻辑处理)
|
||
// 可能需要在 user_purchases 表中记录,或更新 users.purchased_sections
|
||
console.log('[Wechat Notify] ✅ 章节权限已开通:', { userId, sectionId: productId })
|
||
}
|
||
|
||
// === ✅ 3. 分配佣金(如果有推荐人) ===
|
||
try {
|
||
// 查询用户的推荐人(从 referral_bindings)
|
||
const userRows = await query(`
|
||
SELECT u.id, rb.referrer_id, rb.status
|
||
FROM users u
|
||
LEFT JOIN referral_bindings rb ON rb.referee_id = u.id AND rb.status = 'active' AND rb.expiry_date > NOW()
|
||
WHERE u.id = ?
|
||
LIMIT 1
|
||
`, [userId]) as any[]
|
||
|
||
if (userRows.length > 0 && userRows[0].referrer_id) {
|
||
const referrerId = userRows[0].referrer_id
|
||
const commissionRate = 0.9 // 90% 佣金比例
|
||
const commissionAmount = parseFloat((amount * commissionRate).toFixed(2))
|
||
|
||
// 更新推荐人的 pending_earnings
|
||
await query(`
|
||
UPDATE users
|
||
SET pending_earnings = pending_earnings + ?
|
||
WHERE id = ?
|
||
`, [commissionAmount, referrerId])
|
||
|
||
// 更新绑定状态为已转化
|
||
await query(`
|
||
UPDATE referral_bindings
|
||
SET status = 'converted',
|
||
conversion_date = NOW(),
|
||
commission_amount = ?
|
||
WHERE referee_id = ? AND status = 'active'
|
||
`, [commissionAmount, userId])
|
||
|
||
console.log('[Wechat Notify] ✅ 佣金已分配:', {
|
||
referrerId,
|
||
commissionAmount,
|
||
orderId
|
||
})
|
||
} else {
|
||
console.log('[Wechat Notify] ℹ️ 该用户无推荐人,无需分配佣金')
|
||
}
|
||
} catch (commErr) {
|
||
console.error('[Wechat Notify] ❌ 分配佣金失败:', commErr)
|
||
// 不中断主流程
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('[Wechat Notify] ❌ 订单处理失败:', error)
|
||
// 不中断,继续返回成功响应给微信(避免重复回调)
|
||
}
|
||
} else {
|
||
console.log("[Wechat Notify] 支付失败:", notifyResult)
|
||
}
|
||
|
||
// 返回成功响应
|
||
return new NextResponse(gateway.successResponse(), {
|
||
headers: { "Content-Type": "application/xml" },
|
||
})
|
||
|
||
} catch (error) {
|
||
if (error instanceof SignatureError) {
|
||
console.error("[Wechat Notify] 签名验证失败")
|
||
return new NextResponse(gateway.failResponse(), {
|
||
headers: { "Content-Type": "application/xml" },
|
||
})
|
||
}
|
||
throw error
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error("[Wechat Notify] 处理失败:", error)
|
||
|
||
// 返回失败响应
|
||
const failXml = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[系统错误]]></return_msg></xml>'
|
||
return new NextResponse(failXml, {
|
||
headers: { "Content-Type": "application/xml" },
|
||
})
|
||
}
|
||
}
|