Files
soul-yongping/app/api/payment/wechat/notify/route.ts

157 lines
5.7 KiB
TypeScript
Raw Normal View History

/**
* 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 {
// 查询用户的推荐人
const userRows = await query(`
SELECT u.id, u.referred_by, 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" },
})
}
}