Files
soul-yongping/next-project/app/api/payment/create-order/route.ts
2026-02-09 14:43:35 +08:00

175 lines
5.1 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
* 基于 Universal_Payment_Module v4.0 设计
*
* POST /api/payment/create-order
*/
import { type NextRequest, NextResponse } from "next/server"
import {
PaymentFactory,
generateOrderSn,
generateTradeSn,
yuanToFen,
getNotifyUrl,
getReturnUrl,
} from "@/lib/payment"
import { query } from "@/lib/db"
// 确保网关已注册
import "@/lib/payment/alipay"
import "@/lib/payment/wechat"
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { userId, type, sectionId, sectionTitle, amount, paymentMethod, referralCode } = body
// 验证必要参数
if (!userId || !type || !amount || !paymentMethod) {
return NextResponse.json(
{ code: 400, message: "缺少必要参数", data: null },
{ status: 400 }
)
}
// 生成订单号
const orderSn = generateOrderSn()
const tradeSn = generateTradeSn()
// 创建订单对象
const order = {
orderSn,
tradeSn,
userId,
type, // "section" | "fullbook"
sectionId: type === "section" ? sectionId : undefined,
sectionTitle: type === "section" ? sectionTitle : undefined,
amount,
paymentMethod, // "wechat" | "alipay" | "usdt" | "paypal"
referralCode,
status: "created",
createdAt: new Date().toISOString(),
expireAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30分钟
}
// === 💾 插入订单到数据库 ===
try {
// 获取用户 openId如果有
let openId = null
try {
const userRows = await query('SELECT open_id FROM users WHERE id = ?', [userId]) as any[]
if (userRows.length > 0) {
openId = userRows[0].open_id
}
} catch (e) {
console.warn('[Payment] 获取 openId 失败:', e)
}
const productType = type === 'section' ? 'section' : 'fullbook'
const productId = type === 'section' ? sectionId : 'fullbook'
const description = type === 'section'
? `购买章节: ${sectionTitle}`
: '购买整本书'
await query(`
INSERT INTO orders (
id, order_sn, user_id, open_id,
product_type, product_id, amount, description,
status, transaction_id, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, [
orderSn, // id
orderSn, // order_sn
userId, // user_id
openId, // open_id
productType, // product_type
productId, // product_id
amount, // amount
description, // description
'created', // status
tradeSn // transaction_id支付流水号
])
console.log('[Payment] ✅ 订单已插入数据库:', { orderSn, userId, amount })
} catch (dbError) {
console.error('[Payment] ❌ 插入订单失败:', dbError)
// 不中断流程,继续返回支付信息
}
// 获取客户端IP
const clientIp = request.headers.get("x-forwarded-for")
|| request.headers.get("x-real-ip")
|| "127.0.0.1"
// 根据支付方式创建支付网关
let paymentData = null
const goodsTitle = type === "section" ? `购买章节: ${sectionTitle}` : "购买整本书"
// 确定网关类型
let gateway: string
if (paymentMethod === "alipay") {
gateway = "alipay_wap"
} else if (paymentMethod === "wechat") {
gateway = "wechat_native"
} else {
gateway = paymentMethod
}
try {
// 创建支付网关
const paymentGateway = PaymentFactory.create(gateway)
// 创建交易
const tradeResult = await paymentGateway.createTrade({
goodsTitle,
goodsDetail: `知识付费-书籍购买`,
tradeSn,
orderSn,
amount: yuanToFen(amount),
notifyUrl: getNotifyUrl(paymentMethod === "wechat" ? "wechat" : "alipay"),
returnUrl: getReturnUrl(orderSn),
createIp: clientIp.split(",")[0].trim(),
platformType: paymentMethod === "wechat" ? "native" : "wap",
})
paymentData = {
type: tradeResult.type,
payload: tradeResult.payload,
tradeSn: tradeResult.tradeSn,
expiration: tradeResult.expiration,
}
} catch (gatewayError) {
console.error("[Payment] Gateway error:", gatewayError)
// 如果网关创建失败,返回模拟数据(开发测试用)
paymentData = {
type: "qrcode",
payload: `mock://pay/${paymentMethod}/${orderSn}`,
tradeSn,
expiration: 1800,
}
}
return NextResponse.json({
code: 200,
message: "订单创建成功",
data: {
...order,
paymentData,
gateway, // 返回网关信息,用于前端轮询时指定
},
})
} catch (error) {
console.error("[Payment] Create order error:", error)
return NextResponse.json(
{
code: 500,
message: error instanceof Error ? error.message : "服务器错误",
data: null,
},
{ status: 500 }
)
}
}