feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API

主要更新:
1. 按H5网页端完全重构匹配功能(match页面)
   - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募
   - 资源对接等类型弹出手机号/微信号输入框
   - 去掉重新匹配按钮,改为返回按钮

2. 修复所有卡片对齐和宽度问题
   - 目录页附录卡片居中
   - 首页阅读进度卡片满宽度
   - 我的页面菜单卡片对齐
   - 推广中心分享卡片统一宽度

3. 修复目录页图标和文字对齐
   - section-icon固定40rpx宽高
   - section-title与图标垂直居中

4. 更新真实完整文章标题(62篇)
   - 从book目录读取真实markdown文件名
   - 替换之前的简化标题

5. 新增文章数据API
   - /api/db/chapters - 获取完整书籍结构
   - 支持按ID获取单篇文章内容
This commit is contained in:
卡若
2026-01-21 15:49:12 +08:00
parent 1ee25e3dab
commit b60edb3d47
197 changed files with 34430 additions and 7345 deletions

View File

@@ -1,8 +1,19 @@
/**
* 支付宝回调通知 API
* 基于 Universal_Payment_Module v4.0 设计
*
* POST /api/payment/alipay/notify
*/
import { type NextRequest, NextResponse } from "next/server"
import { AlipayService } from "@/lib/payment/alipay"
import { PaymentFactory, SignatureError } from "@/lib/payment"
// 确保网关已注册
import "@/lib/payment/alipay"
export async function POST(request: NextRequest) {
try {
// 获取表单数据
const formData = await request.formData()
const params: Record<string, string> = {}
@@ -10,39 +21,54 @@ export async function POST(request: NextRequest) {
params[key] = value.toString()
})
// 初始化支付宝服务
const alipay = new AlipayService({
appId: process.env.ALIPAY_APP_ID || "wx432c93e275548671",
partnerId: process.env.ALIPAY_PARTNER_ID || "2088511801157159",
key: process.env.ALIPAY_KEY || "lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp",
returnUrl: "",
notifyUrl: "",
console.log("[Alipay Notify] 收到回调:", {
out_trade_no: params.out_trade_no,
trade_status: params.trade_status,
total_amount: params.total_amount,
})
// 验证签名
const isValid = alipay.verifySign(params)
// 创建支付宝网关
const gateway = PaymentFactory.create("alipay_wap")
if (!isValid) {
console.error("[v0] Alipay signature verification failed")
return new NextResponse("fail")
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,
})
// TODO: 更新订单状态
// await OrderService.updateStatus(notifyResult.tradeSn, 'paid')
// TODO: 解锁内容/开通权限
// await ContentService.unlockForUser(notifyResult.attach?.userId, notifyResult.attach?.productId)
// TODO: 分配佣金(如果有推荐人)
// if (notifyResult.attach?.referralCode) {
// await ReferralService.distributeCommission(notifyResult)
// }
} 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
}
const { out_trade_no, trade_status, buyer_id, total_amount } = params
// 只处理支付成功的通知
if (trade_status === "TRADE_SUCCESS" || trade_status === "TRADE_FINISHED") {
console.log("[v0] Alipay payment success:", {
orderId: out_trade_no,
amount: total_amount,
buyerId: buyer_id,
})
// TODO: 更新订单状态、解锁内容、分配佣金
}
return new NextResponse("success")
} catch (error) {
console.error("[v0] Alipay notify error:", error)
console.error("[Alipay Notify] 处理失败:", error)
return new NextResponse("fail")
}
}

View File

@@ -1,3 +1,8 @@
/**
* 支付回调接口
* 开发: 卡若
* 技术支持: 存客宝
*/
import { type NextRequest, NextResponse } from "next/server"
export async function POST(request: NextRequest) {
@@ -5,7 +10,7 @@ export async function POST(request: NextRequest) {
const body = await request.json()
const { orderId, status, transactionId, amount, paymentMethod, signature } = body
console.log("[v0] Payment callback received:", {
console.log("[Karuo] Payment callback received:", {
orderId,
status,
transactionId,
@@ -32,7 +37,7 @@ export async function POST(request: NextRequest) {
// Update order status
if (status === "success") {
// Grant access
console.log("[v0] Payment successful, granting access for order:", orderId)
console.log("[Karuo] Payment successful, granting access for order:", orderId)
}
return NextResponse.json({
@@ -40,7 +45,7 @@ export async function POST(request: NextRequest) {
message: "回调处理成功",
})
} catch (error) {
console.error("[v0] Payment callback error:", error)
console.error("[Karuo] Payment callback error:", error)
return NextResponse.json({ code: 500, message: "服务器错误" }, { status: 500 })
}
}

View File

@@ -1,23 +1,45 @@
/**
* 创建支付订单 API
* 基于 Universal_Payment_Module v4.0 设计
*
* POST /api/payment/create-order
*/
import { type NextRequest, NextResponse } from "next/server"
import { AlipayService } from "@/lib/payment/alipay"
import { WechatPayService } from "@/lib/payment/wechat"
import {
PaymentFactory,
generateOrderSn,
generateTradeSn,
yuanToFen,
getNotifyUrl,
getReturnUrl,
} from "@/lib/payment"
// 确保网关已注册
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
// Validate required fields
// 验证必要参数
if (!userId || !type || !amount || !paymentMethod) {
return NextResponse.json({ code: 400, message: "缺少必要参数" }, { status: 400 })
return NextResponse.json(
{ code: 400, message: "缺少必要参数", data: null },
{ status: 400 }
)
}
// Generate order ID
const orderId = `ORDER_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
// 生成订单号
const orderSn = generateOrderSn()
const tradeSn = generateTradeSn()
// Create order object
// 创建订单对象
const order = {
orderId,
orderSn,
tradeSn,
userId,
type, // "section" | "fullbook"
sectionId: type === "section" ? sectionId : undefined,
@@ -25,64 +47,83 @@ export async function POST(request: NextRequest) {
amount,
paymentMethod, // "wechat" | "alipay" | "usdt" | "paypal"
referralCode,
status: "pending", // pending | completed | failed | refunded
status: "created",
createdAt: new Date().toISOString(),
expireAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30 minutes
expireAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30分钟
}
// According to the payment method, create a payment order
// 获取客户端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") {
const alipay = new AlipayService({
appId: process.env.ALIPAY_APP_ID || "wx432c93e275548671",
partnerId: process.env.ALIPAY_PARTNER_ID || "2088511801157159",
key: process.env.ALIPAY_KEY || "lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp",
returnUrl: process.env.ALIPAY_RETURN_URL || `${process.env.NEXT_PUBLIC_BASE_URL}/payment/success`,
notifyUrl: process.env.ALIPAY_NOTIFY_URL || `${process.env.NEXT_PUBLIC_BASE_URL}/api/payment/alipay/notify`,
})
paymentData = alipay.createOrder({
outTradeNo: orderId,
subject: type === "section" ? `购买章节: ${sectionTitle}` : "购买整本书",
totalAmount: amount,
body: `知识付费-书籍购买`,
})
gateway = "alipay_wap"
} else if (paymentMethod === "wechat") {
const wechat = new WechatPayService({
appId: process.env.WECHAT_APP_ID || "wx432c93e275548671",
appSecret: process.env.WECHAT_APP_SECRET || "25b7e7fdb7998e5107e242ebb6ddabd0",
mchId: process.env.WECHAT_MCH_ID || "1318592501",
apiKey: process.env.WECHAT_API_KEY || "wx3e31b068be59ddc131b068be59ddc2",
notifyUrl: process.env.WECHAT_NOTIFY_URL || `${process.env.NEXT_PUBLIC_BASE_URL}/api/payment/wechat/notify`,
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",
})
const clientIp = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "127.0.0.1"
paymentData = {
type: tradeResult.type,
payload: tradeResult.payload,
tradeSn: tradeResult.tradeSn,
expiration: tradeResult.expiration,
}
paymentData = await wechat.createOrder({
outTradeNo: orderId,
body: type === "section" ? `购买章节: ${sectionTitle}` : "购买整本书",
totalFee: amount,
spbillCreateIp: clientIp.split(",")[0].trim(),
})
} catch (gatewayError) {
console.error("[Payment] Gateway error:", gatewayError)
// 如果网关创建失败,返回模拟数据(开发测试用)
paymentData = {
type: "qrcode",
payload: `mock://pay/${paymentMethod}/${orderSn}`,
tradeSn,
expiration: 1800,
}
}
return NextResponse.json({
code: 0,
code: 200,
message: "订单创建成功",
data: {
...order,
paymentData,
gateway, // 返回网关信息,用于前端轮询时指定
},
})
} catch (error) {
console.error("[v0] Create order error:", error)
console.error("[Payment] Create order error:", error)
return NextResponse.json(
{
code: 500,
message: error instanceof Error ? error.message : "服务器错误",
data: null,
},
{ status: 500 },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,37 @@
/**
* 获取可用支付方式 API
* 基于 Universal_Payment_Module v4.0 设计
*
* GET /api/payment/methods
*/
import { NextResponse } from "next/server"
import { PaymentFactory } from "@/lib/payment"
// 确保网关已注册
import "@/lib/payment/alipay"
import "@/lib/payment/wechat"
export async function GET() {
try {
const methods = PaymentFactory.getEnabledGateways()
return NextResponse.json({
code: 200,
message: "success",
data: {
methods,
},
})
} catch (error) {
console.error("[Payment] Get methods error:", error)
return NextResponse.json(
{
code: 500,
message: error instanceof Error ? error.message : "服务器错误",
data: null,
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,143 @@
/**
* 查询支付状态 API
* 基于 Universal_Payment_Module v4.0 设计
*
* GET /api/payment/query?tradeSn=xxx&gateway=wechat_native|alipay_wap
*/
import { type NextRequest, NextResponse } from "next/server"
import { PaymentFactory } from "@/lib/payment"
// 确保网关已注册
import "@/lib/payment/alipay"
import "@/lib/payment/wechat"
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const tradeSn = searchParams.get("tradeSn")
const gateway = searchParams.get("gateway") // 可选:指定支付网关
if (!tradeSn) {
return NextResponse.json(
{ code: 400, message: "缺少交易号", data: null },
{ status: 400 }
)
}
console.log("[Payment Query] 查询交易状态:", { tradeSn, gateway })
let wechatResult = null
let alipayResult = null
// 如果指定了网关,只查询该网关
if (gateway) {
try {
const paymentGateway = PaymentFactory.create(gateway)
const result = await paymentGateway.queryTrade(tradeSn)
return NextResponse.json({
code: 200,
message: "success",
data: {
tradeSn: result?.tradeSn || tradeSn,
status: result?.status || "paying",
platformSn: result?.platformSn || null,
payAmount: result?.payAmount || null,
payTime: result?.payTime || null,
gateway,
},
})
} catch (e) {
console.log(`[Payment Query] ${gateway} 查询失败:`, e)
return NextResponse.json({
code: 200,
message: "success",
data: {
tradeSn,
status: "paying",
platformSn: null,
payAmount: null,
payTime: null,
gateway,
},
})
}
}
// 没有指定网关时,先查询微信
try {
const wechatGateway = PaymentFactory.create("wechat_native")
wechatResult = await wechatGateway.queryTrade(tradeSn)
if (wechatResult && wechatResult.status === "paid") {
console.log("[Payment Query] 微信支付成功:", { tradeSn, status: wechatResult.status })
return NextResponse.json({
code: 200,
message: "success",
data: {
tradeSn: wechatResult.tradeSn,
status: wechatResult.status,
platformSn: wechatResult.platformSn,
payAmount: wechatResult.payAmount,
payTime: wechatResult.payTime,
gateway: "wechat_native",
},
})
}
} catch (e) {
console.log("[Payment Query] 微信查询异常:", e)
}
// 再查询支付宝
try {
const alipayGateway = PaymentFactory.create("alipay_wap")
alipayResult = await alipayGateway.queryTrade(tradeSn)
if (alipayResult && alipayResult.status === "paid") {
console.log("[Payment Query] 支付宝支付成功:", { tradeSn, status: alipayResult.status })
return NextResponse.json({
code: 200,
message: "success",
data: {
tradeSn: alipayResult.tradeSn,
status: alipayResult.status,
platformSn: alipayResult.platformSn,
payAmount: alipayResult.payAmount,
payTime: alipayResult.payTime,
gateway: "alipay_wap",
},
})
}
} catch (e) {
console.log("[Payment Query] 支付宝查询异常:", e)
}
// 如果都未支付,优先返回微信的状态(因为更可靠)
const result = wechatResult || alipayResult
// 返回等待支付状态
return NextResponse.json({
code: 200,
message: "success",
data: {
tradeSn,
status: result?.status || "paying",
platformSn: result?.platformSn || null,
payAmount: result?.payAmount || null,
payTime: result?.payTime || null,
gateway: null,
},
})
} catch (error) {
console.error("[Payment Query] Error:", error)
return NextResponse.json(
{
code: 500,
message: error instanceof Error ? error.message : "服务器错误",
data: null,
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,53 @@
/**
* 查询订单支付状态 API
* 基于 Universal_Payment_Module v4.0 设计
*
* GET /api/payment/status/{orderSn}
*/
import { type NextRequest, NextResponse } from "next/server"
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ orderSn: string }> }
) {
try {
const { orderSn } = await params
if (!orderSn) {
return NextResponse.json(
{ code: 400, message: "缺少订单号", data: null },
{ status: 400 }
)
}
// TODO: 从数据库查询订单状态
// const order = await OrderService.getByOrderSn(orderSn)
// 模拟返回数据(开发测试用)
const mockOrder = {
orderSn,
status: "created", // created | paying | paid | closed | refunded
paidAmount: null,
paidAt: null,
paymentMethod: null,
tradeSn: null,
}
return NextResponse.json({
code: 200,
message: "success",
data: mockOrder,
})
} catch (error) {
console.error("[Payment] Query status error:", error)
return NextResponse.json(
{
code: 500,
message: error instanceof Error ? error.message : "服务器错误",
data: null,
},
{ status: 500 }
)
}
}

View File

@@ -1,3 +1,8 @@
/**
* 支付验证接口
* 开发: 卡若
* 技术支持: 存客宝
*/
import { type NextRequest, NextResponse } from "next/server"
export async function POST(request: NextRequest) {
@@ -11,7 +16,7 @@ export async function POST(request: NextRequest) {
// In production, verify with payment gateway API
// For now, simulate verification
console.log("[v0] Verifying payment:", { orderId, paymentMethod, transactionId })
console.log("[Karuo] Verifying payment:", { orderId, paymentMethod, transactionId })
// Simulate verification delay
await new Promise((resolve) => setTimeout(resolve, 500))
@@ -40,7 +45,7 @@ export async function POST(request: NextRequest) {
})
}
} catch (error) {
console.error("[v0] Verify payment error:", error)
console.error("[Karuo] Verify payment error:", error)
return NextResponse.json({ code: 500, message: "服务器错误" }, { status: 500 })
}
}

View File

@@ -1,59 +1,74 @@
/**
* 微信支付回调通知 API
* 基于 Universal_Payment_Module v4.0 设计
*
* POST /api/payment/wechat/notify
*/
import { type NextRequest, NextResponse } from "next/server"
import { WechatPayService } from "@/lib/payment/wechat"
import { PaymentFactory, SignatureError } from "@/lib/payment"
// 确保网关已注册
import "@/lib/payment/wechat"
export async function POST(request: NextRequest) {
try {
// 获取XML原始数据
const xmlData = await request.text()
const wechat = new WechatPayService({
appId: process.env.WECHAT_APP_ID || "wx432c93e275548671",
appSecret: process.env.WECHAT_APP_SECRET || "25b7e7fdb7998e5107e242ebb6ddabd0",
mchId: process.env.WECHAT_MCH_ID || "1318592501",
apiKey: process.env.WECHAT_API_KEY || "wx3e31b068be59ddc131b068be59ddc2",
notifyUrl: "",
})
console.log("[Wechat Notify] 收到回调:", xmlData.slice(0, 200))
// 解析XML数据
const params = await wechat["parseXML"](xmlData)
// 创建微信支付网关
const gateway = PaymentFactory.create("wechat_native")
// 验证签名
const isValid = wechat.verifySign(params)
try {
// 解析并验证回调数据
const notifyResult = gateway.parseNotify(xmlData)
if (!isValid) {
console.error("[v0] WeChat signature verification failed")
return new NextResponse(
"<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>",
{
headers: { "Content-Type": "application/xml" },
},
)
}
if (notifyResult.status === "paid") {
console.log("[Wechat Notify] 支付成功:", {
tradeSn: notifyResult.tradeSn,
platformSn: notifyResult.platformSn,
amount: notifyResult.payAmount / 100, // 转换为元
payTime: notifyResult.payTime,
})
const { out_trade_no, result_code, total_fee, openid } = params
// TODO: 更新订单状态
// await OrderService.updateStatus(notifyResult.tradeSn, 'paid')
if (result_code === "SUCCESS") {
console.log("[v0] WeChat payment success:", {
orderId: out_trade_no,
amount: Number.parseInt(total_fee) / 100,
openid,
// TODO: 解锁内容/开通权限
// await ContentService.unlockForUser(notifyResult.attach?.userId, notifyResult.attach?.productId)
// TODO: 分配佣金(如果有推荐人)
// if (notifyResult.attach?.referralCode) {
// await ReferralService.distributeCommission(notifyResult)
// }
} else {
console.log("[Wechat Notify] 支付失败:", notifyResult)
}
// 返回成功响应
return new NextResponse(gateway.successResponse(), {
headers: { "Content-Type": "application/xml" },
})
// TODO: 更新订单状态、解锁内容、分配佣金
} catch (error) {
if (error instanceof SignatureError) {
console.error("[Wechat Notify] 签名验证失败")
return new NextResponse(gateway.failResponse(), {
headers: { "Content-Type": "application/xml" },
})
}
throw error
}
return new NextResponse(
"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>",
{
headers: { "Content-Type": "application/xml" },
},
)
} catch (error) {
console.error("[v0] WeChat notify error:", error)
return new NextResponse(
"<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[系统错误]]></return_msg></xml>",
{
headers: { "Content-Type": "application/xml" },
},
)
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" },
})
}
}