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

156 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
* 基于 Universal_Payment_Module v4.0 设计
*
* POST /api/payment/alipay/notify
*/
import { type NextRequest, NextResponse } from "next/server"
import { PaymentFactory, SignatureError } from "@/lib/payment"
import { query } from "@/lib/db"
// 确保网关已注册
import "@/lib/payment/alipay"
export async function POST(request: NextRequest) {
try {
// 获取表单数据
const formData = await request.formData()
const params: Record<string, string> = {}
formData.forEach((value, key) => {
params[key] = value.toString()
})
console.log("[Alipay Notify] 收到回调:", {
out_trade_no: params.out_trade_no,
trade_status: params.trade_status,
total_amount: params.total_amount,
})
// 创建支付宝网关
const gateway = PaymentFactory.create("alipay_wap")
try {
// 解析并验证回调数据
const notifyResult = gateway.parseNotify(params)
if (notifyResult.status === "paid") {
console.log("[Alipay 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('[Alipay 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('[Alipay Notify] ✅ 订单状态已更新:', { orderId, status: 'paid' })
// === ✅ 2. 解锁内容/开通权限 ===
if (productType === 'fullbook') {
// 购买全书
await query('UPDATE users SET has_full_book = 1 WHERE id = ?', [userId])
console.log('[Alipay Notify] ✅ 全书权限已开通:', userId)
} else if (productType === 'section' && productId) {
// 购买单个章节
console.log('[Alipay 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('[Alipay Notify] ✅ 佣金已分配:', {
referrerId,
commissionAmount,
orderId
})
} else {
console.log('[Alipay Notify] 该用户无推荐人,无需分配佣金')
}
} catch (commErr) {
console.error('[Alipay Notify] ❌ 分配佣金失败:', commErr)
// 不中断主流程
}
}
} catch (error) {
console.error('[Alipay Notify] ❌ 订单处理失败:', error)
// 不中断,继续返回成功响应给支付宝(避免重复回调)
}
} else {
console.log("[Alipay Notify] 非支付成功状态:", notifyResult.status)
}
// 返回成功响应
return new NextResponse(gateway.successResponse())
} catch (error) {
if (error instanceof SignatureError) {
console.error("[Alipay Notify] 签名验证失败")
return new NextResponse(gateway.failResponse())
}
throw error
}
} catch (error) {
console.error("[Alipay Notify] 处理失败:", error)
return new NextResponse("fail")
}
}