"use client" import type React from "react" import { useState, useEffect, useCallback, useRef } from "react" import { X, CheckCircle, Bitcoin, Globe, Copy, Check, QrCode, Shield, Users, Loader2, AlertCircle } from "lucide-react" import { useStore } from "@/lib/store" import QRCode from "qrcode" const WechatIcon = () => ( ) type PaymentMethod = "wechat" | "alipay" | "usdt" | "paypal" | "stripe" | "bank" interface PaymentModalProps { isOpen: boolean onClose: () => void type: "section" | "fullbook" sectionId?: string sectionTitle?: string amount: number onSuccess: () => void } // 支付状态类型 type PaymentState = "idle" | "creating" | "paying" | "polling" | "success" | "error" export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, amount, onSuccess }: PaymentModalProps) { const [paymentMethod, setPaymentMethod] = useState("wechat") const [paymentState, setPaymentState] = useState("idle") const [errorMessage, setErrorMessage] = useState("") const [showQRCode, setShowQRCode] = useState(false) const [qrCodeDataUrl, setQrCodeDataUrl] = useState("") const [paymentUrl, setPaymentUrl] = useState("") const [orderSn, setOrderSn] = useState("") const [tradeSn, setTradeSn] = useState("") const [currentGateway, setCurrentGateway] = useState("") // 当前支付网关 const [copied, setCopied] = useState(false) const pollingRef = useRef(null) const pollingCountRef = useRef(0) const { purchaseSection, purchaseFullBook, user, settings } = useStore() const paymentConfig = settings?.paymentMethods || { wechat: { enabled: true, qrCode: "", account: "", groupQrCode: "" }, alipay: { enabled: true, qrCode: "", account: "" }, usdt: { enabled: true, network: "TRC20", address: "", exchangeRate: 7.2 }, paypal: { enabled: false, email: "", exchangeRate: 7.2 }, } const usdtAmount = (amount / (paymentConfig.usdt?.exchangeRate || 7.2)).toFixed(2) const paypalAmount = (amount / (paymentConfig.paypal?.exchangeRate || 7.2)).toFixed(2) // 清理轮询 const clearPolling = useCallback(() => { if (pollingRef.current) { clearInterval(pollingRef.current) pollingRef.current = null } pollingCountRef.current = 0 }, []) // 重置状态 const resetState = useCallback(() => { setPaymentState("idle") setShowQRCode(false) setQrCodeDataUrl("") setPaymentUrl("") setOrderSn("") setTradeSn("") setCurrentGateway("") setErrorMessage("") clearPolling() }, [clearPolling]) useEffect(() => { if (isOpen) { resetState() } return () => { clearPolling() } }, [isOpen, resetState, clearPolling]) // 创建订单并获取支付参数 const createPaymentOrder = async () => { setPaymentState("creating") setErrorMessage("") try { const response = await fetch("/api/payment/create-order", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userId: user?.id || "anonymous", type, sectionId, sectionTitle, amount, paymentMethod, referralCode: user?.referredBy, }), }) const result = await response.json() if (result.code !== 200) { throw new Error(result.message || "创建订单失败") } const { orderSn: newOrderSn, tradeSn: newTradeSn, paymentData, gateway } = result.data setOrderSn(newOrderSn) setTradeSn(newTradeSn) // 保存网关信息用于后续查询 const gatewayId = gateway || (paymentMethod === "wechat" ? "wechat_native" : "alipay_wap") setCurrentGateway(gatewayId) // 根据支付方式处理不同的返回数据 if (paymentData.type === "url") { // URL类型:跳转支付(支付宝WAP/WEB) setPaymentUrl(paymentData.payload) setShowQRCode(true) setPaymentState("paying") // 打开支付页面 window.open(paymentData.payload, "_blank") // 开始轮询支付状态,传递网关信息 startPolling(newTradeSn, gatewayId) } else if (paymentData.type === "qrcode") { // 二维码类型:扫码支付(微信Native/支付宝QR) const qrUrl = paymentData.payload // 生成二维码图片 const dataUrl = await QRCode.toDataURL(qrUrl, { width: 200, margin: 2, color: { dark: "#000000", light: "#ffffff", }, }) setQrCodeDataUrl(dataUrl) setPaymentUrl(qrUrl) setShowQRCode(true) setPaymentState("paying") // 开始轮询支付状态,传递网关信息 startPolling(newTradeSn, gatewayId) } else if (paymentData.type === "json") { // JSON类型:JSAPI支付(需要调用JS SDK) console.log("JSAPI支付参数:", paymentData.payload) // 这里需要调用微信JS SDK setPaymentState("error") setErrorMessage("JSAPI支付需要在微信内打开") } } catch (error) { console.error("创建订单失败:", error) setPaymentState("error") setErrorMessage(error instanceof Error ? error.message : "创建订单失败,请重试") } } // 轮询支付状态 const startPolling = (tradeSnToQuery: string, gateway?: string) => { setPaymentState("polling") pollingCountRef.current = 0 pollingRef.current = setInterval(async () => { pollingCountRef.current++ // 最多轮询60次(5分钟) if (pollingCountRef.current > 60) { clearPolling() setPaymentState("error") setErrorMessage("支付超时,请重新发起支付") return } try { // 构建查询URL,包含网关参数以提高查询准确性 let queryUrl = `/api/payment/query?tradeSn=${tradeSnToQuery}` if (gateway) { queryUrl += `&gateway=${gateway}` } const response = await fetch(queryUrl) const result = await response.json() if (result.code === 200 && result.data) { const { status } = result.data if (status === "paid") { // 支付成功 clearPolling() await handlePaymentSuccess() } else if (status === "closed" || status === "refunded") { // 订单已关闭 clearPolling() setPaymentState("error") setErrorMessage("订单已关闭") } // paying状态继续轮询 } } catch (error) { console.error("查询支付状态失败:", error) // 查询失败继续轮询 } }, 5000) // 每5秒查询一次 } // 处理支付成功 const handlePaymentSuccess = async () => { setPaymentState("success") // 调用store更新购买状态 let success = false if (type === "section" && sectionId) { success = await purchaseSection(sectionId, sectionTitle, paymentMethod) } else if (type === "fullbook") { success = await purchaseFullBook(paymentMethod) } // 打开社群二维码 const groupUrl = paymentConfig.wechat?.groupQrCode if (groupUrl) { setTimeout(() => { window.open(groupUrl, "_blank") }, 800) } // 关闭弹窗 setTimeout(() => { onSuccess() onClose() resetState() }, 2500) } // 手动确认支付(用于轮询失效的情况) const handleManualConfirm = async () => { if (!tradeSn) return setPaymentState("polling") try { // 构建查询URL,包含网关参数 let queryUrl = `/api/payment/query?tradeSn=${tradeSn}` if (currentGateway) { queryUrl += `&gateway=${currentGateway}` } const response = await fetch(queryUrl) const result = await response.json() if (result.code === 200 && result.data?.status === "paid") { await handlePaymentSuccess() } else { setErrorMessage("未检测到支付,请确认是否已完成支付") setPaymentState("paying") } } catch (error) { setErrorMessage("查询支付状态失败,请稍后重试") setPaymentState("paying") } } const handleCopyAddress = (address: string) => { navigator.clipboard.writeText(address) setCopied(true) setTimeout(() => setCopied(false), 2000) } // 处理开始支付 const handlePayment = async () => { // USDT和PayPal使用旧的手动确认流程 if (paymentMethod === "usdt" || paymentMethod === "paypal") { setShowQRCode(true) return } // 微信和支付宝使用新的API流程 await createPaymentOrder() } // USDT/PayPal手动确认 const handleCryptoConfirm = async () => { setPaymentState("creating") // 模拟确认支付(实际需要人工审核) await new Promise((resolve) => setTimeout(resolve, 1000)) let success = false if (type === "section" && sectionId) { success = await purchaseSection(sectionId, sectionTitle, paymentMethod) } else if (type === "fullbook") { success = await purchaseFullBook(paymentMethod) } if (success) { await handlePaymentSuccess() } else { setPaymentState("error") setErrorMessage("确认支付失败,请联系客服") } } if (!isOpen) return null const paymentMethods: { id: PaymentMethod name: string icon: React.ReactNode color: string iconBg: string enabled: boolean extra?: string }[] = [ { id: "wechat", name: "微信支付", icon: , color: "#07C160", iconBg: "rgba(7, 193, 96, 0.15)", enabled: paymentConfig.wechat?.enabled ?? true, }, { id: "usdt", name: `USDT (${paymentConfig.usdt?.network || "TRC20"})`, icon: , color: "#26A17B", iconBg: "rgba(38, 161, 123, 0.15)", enabled: paymentConfig.usdt?.enabled ?? false, extra: `≈ $${usdtAmount}`, }, { id: "paypal", name: "PayPal", icon: , color: "#003087", iconBg: "rgba(0, 48, 135, 0.15)", enabled: paymentConfig.paypal?.enabled ?? false, extra: `≈ $${paypalAmount}`, }, ] const availableMethods = paymentMethods.filter((m) => m.enabled) // 二维码/详情页面 if (showQRCode) { const isCrypto = paymentMethod === "usdt" const isPaypal = paymentMethod === "paypal" const isWechat = paymentMethod === "wechat" const isAlipay = paymentMethod === "alipay" let address = "" let displayAmount = `¥${amount.toFixed(2)}` let title = "扫码支付" let hint = "支付完成后,系统将自动确认" if (isCrypto) { address = paymentConfig.usdt?.address || "" displayAmount = `$${usdtAmount} USDT` title = "USDT支付" hint = "请转账到以下地址,完成后点击确认" } else if (isPaypal) { address = paymentConfig.paypal?.email || "" displayAmount = `$${paypalAmount} USD` title = "PayPal支付" hint = "请转账到以下PayPal账户" } else if (isWechat) { title = "微信支付" hint = "请使用微信扫描二维码支付" } else if (isAlipay) { title = "支付宝支付" hint = paymentUrl?.startsWith("http") ? "已打开支付页面,请在新窗口完成支付" : "请使用支付宝扫描二维码支付" } return (
{/* 顶部把手 - 仅移动端 */}
{/* 标题 */}

{title}

{/* 金额显示 */}

{displayAmount}

{/* 错误提示 */} {errorMessage && (

{errorMessage}

)} {/* QR Code Display - 微信/支付宝 */} {(isWechat || isAlipay) && (
{paymentState === "creating" ? (
正在生成二维码...
) : qrCodeDataUrl ? ( 支付二维码 ) : paymentUrl?.startsWith("http") ? (
已在新窗口打开支付页面
) : (
二维码加载中...
)}

{hint}

{/* 轮询状态指示 */} {paymentState === "polling" && (
正在等待支付结果...
)}
)} {/* Crypto/PayPal Address */} {(isCrypto || isPaypal) && (

{isCrypto ? `收款地址 (${paymentConfig.usdt?.network})` : "PayPal账户"}

{address || "请在后台配置收款地址"}

{address && ( )}

{hint}

)} {/* 提示信息 */}

支付完成后,系统将自动开通阅读权限

{/* Action Buttons */}
{(isCrypto || isPaypal) ? ( ) : ( )}
) } // 支付成功页面 if (paymentState === "success") { return (
{/* 成功动画 */}

支付成功

{type === "fullbook" ? "您已解锁全部内容" : "您已解锁本节内容"}

{paymentConfig.wechat?.groupQrCode && (
正在跳转到读者社群...
)}
) } // 主支付选择页面 return (
{/* 顶部把手 - 仅移动端 */}
{/* Header */}

确认支付

{type === "fullbook" ? "购买整本书,解锁全部内容" : `购买: ${sectionTitle}`}

{/* Amount */}

支付金额

¥{amount.toFixed(2)}

{(paymentMethod === "usdt" || paymentMethod === "paypal") && (

≈ ${paymentMethod === "usdt" ? usdtAmount : paypalAmount} USD

)} {user?.referredBy && (
推荐人将获得 {settings?.distributorShare || 90}% 返佣
)}
{/* Payment Methods */}

选择支付方式

{availableMethods.map((method) => ( ))} {availableMethods.length === 0 && (

暂无可用支付方式

)}
{/* Submit Button */}

支付即表示同意《用户协议》和《隐私政策》

) }