2025-12-24 16:42:58 +08:00

293 lines
12 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.

'use client'
import { Suspense, useEffect, useState } from 'react'
import { useSearchParams } from 'next/navigation'
import { CheckCircle, XCircle, Loader2 } from 'lucide-react'
import Link from 'next/link'
import Image from 'next/image'
export const dynamic = 'force-dynamic'
function PaymentSuccessContent() {
const searchParams = useSearchParams()
const orderNo = searchParams.get('order_no')
const [orderStatus, setOrderStatus] = useState<'loading' | 'success' | 'failed' | 'pending'>('loading')
const [orderInfo, setOrderInfo] = useState<any>(null)
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'error'>('idle')
useEffect(() => {
if (!orderNo) {
setOrderStatus('failed')
return
}
const checkOrderStatus = async () => {
try {
const response = await fetch(`/api/payment/status?order_no=${orderNo}`)
const data = await response.json()
if (data.success) {
setOrderInfo(data.order)
setOrderStatus(
data.order.status === 'paid'
? 'success'
: data.order.status === 'failed'
? 'failed'
: 'pending'
)
} else {
setOrderStatus('failed')
}
} catch (error) {
console.error('查询订单状态错误:', error)
setOrderStatus('failed')
}
}
checkOrderStatus()
// 如果订单还在pending状态,每3秒轮询一次(最多轮询10次)
let pollCount = 0
const pollInterval = setInterval(() => {
if (pollCount >= 10) {
clearInterval(pollInterval)
return
}
pollCount++
checkOrderStatus()
}, 3000)
return () => clearInterval(pollInterval)
}, [orderNo])
const handleCopy = async (text?: string) => {
if (!text) return
try {
await navigator.clipboard.writeText(text)
setCopyState('copied')
setTimeout(() => setCopyState('idle'), 1500)
} catch (e) {
console.error('复制失败:', e)
setCopyState('error')
setTimeout(() => setCopyState('idle'), 1500)
}
}
const license = orderInfo?.license
const licenseError = orderInfo?.licenseError
return (
<div className="min-h-screen bg-gradient-to-b from-apple-gray-50 to-white flex items-center justify-center px-4 py-12">
<div className="max-w-md w-full">
{/* Logo */}
<div className="text-center mb-8">
<div className="flex justify-center mb-4">
<div className="relative w-16 h-16">
<Image
src="/images/logo.png"
alt="FLNXI Logo"
fill
className="object-contain"
priority
/>
</div>
</div>
</div>
{/* 支付结果卡片 */}
<div className="bg-white rounded-3xl apple-shadow-lg p-8">
{orderStatus === 'loading' && (
<div className="text-center py-8">
<Loader2 className="w-16 h-16 text-apple-blue animate-spin mx-auto mb-4" />
<h2 className="text-2xl font-bold text-apple-gray-900 mb-2">
正在确认支付结果...
</h2>
<p className="text-apple-gray-600">
请稍候,我们正在验证您的支付信息
</p>
</div>
)}
{orderStatus === 'success' && (
<div className="text-center py-8">
<div className="w-16 h-16 bg-gradient-to-r from-green-500 to-emerald-600 rounded-full flex items-center justify-center mx-auto mb-4">
<CheckCircle className="w-8 h-8 text-white" />
</div>
<h2 className="text-3xl font-bold text-apple-gray-900 mb-2">
支付成功!
</h2>
<p className="text-apple-gray-600 mb-6">
感谢您的购买,订单已确认
</p>
{orderInfo && (
<div className="bg-apple-gray-50 rounded-xl p-4 mb-6 text-left">
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-apple-gray-600">订单号:</span>
<span className="font-mono font-semibold">{orderInfo.order_no}</span>
</div>
<div className="flex justify-between">
<span className="text-apple-gray-600">商品名称:</span>
<span className="font-semibold">{orderInfo.product_name}</span>
</div>
<div className="flex justify-between">
<span className="text-apple-gray-600">支付金额:</span>
<span className="font-bold text-green-600">¥{orderInfo.amount}</span>
</div>
{orderInfo.trade_no && (
<div className="flex justify-between">
<span className="text-apple-gray-600">交易号:</span>
<span className="font-mono text-xs">{orderInfo.trade_no}</span>
</div>
)}
</div>
</div>
)}
{/* 激活码信息 */}
{license ? (
<div className="bg-gradient-to-r from-emerald-50 to-green-50 border border-green-200 rounded-2xl p-4 mb-6 text-left apple-shadow">
<div className="flex items-center justify-between mb-3">
<div>
<p className="text-sm text-green-600 font-semibold">激活码已生成</p>
<p className="text-xs text-apple-gray-500">请妥善保存,稍后可在此页面再次查看</p>
</div>
<button
onClick={() => handleCopy(license.activationCode)}
className="text-xs px-3 py-1 rounded-full bg-green-600 text-white hover:bg-green-700 transition"
>
{copyState === 'copied' ? '已复制' : copyState === 'error' ? '复制失败' : '复制'}
</button>
</div>
<div className="bg-white border border-green-100 rounded-xl p-3 space-y-2 text-sm">
<div className="flex justify-between items-center">
<span className="text-apple-gray-600">激活码:</span>
<span className="font-mono font-semibold text-apple-gray-900 text-xs break-all">
{license.activationCode}
</span>
</div>
{license.planName && (
<div className="flex justify-between">
<span className="text-apple-gray-600">版本:</span>
<span className="font-semibold text-apple-gray-900">{license.planName}</span>
</div>
)}
{license.isPermanent === false && license.expiresAt && (
<div className="flex justify-between">
<span className="text-apple-gray-600">到期时间:</span>
<span className="font-mono text-xs text-apple-gray-900">{license.expiresAt}</span>
</div>
)}
{license.isPermanent && (
<div className="flex justify-between">
<span className="text-apple-gray-600">有效期:</span>
<span className="font-semibold text-apple-gray-900">永久有效</span>
</div>
)}
</div>
</div>
) : licenseError ? (
<div className="bg-red-50 border border-red-200 rounded-2xl p-4 mb-6 text-left">
<p className="text-red-700 font-semibold mb-1">激活码生成失败</p>
<p className="text-xs text-apple-gray-600 mb-1">
{licenseError.message || '系统在生成激活码时出现错误,请联系客服处理。'}
</p>
<p className="text-[10px] text-apple-gray-400">
时间:{licenseError.at}
</p>
</div>
) : (
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-4 mb-6 text-left">
<p className="text-amber-700 font-semibold mb-1">激活码生成中</p>
<p className="text-xs text-apple-gray-600">
支付已成功,系统正在生成激活码。请稍等片刻或稍后重新打开此页面查看。
</p>
</div>
)}
<div className="space-y-3">
<Link
href="/"
className="block w-full py-3 bg-gradient-to-r from-apple-blue to-blue-600 text-white rounded-full font-semibold button-hover apple-shadow"
>
返回首页
</Link>
<p className="text-xs text-apple-gray-500">
我们将在1-3个工作日内为您开通服务
</p>
</div>
</div>
)}
{orderStatus === 'pending' && (
<div className="text-center py-8">
<Loader2 className="w-16 h-16 text-orange-500 animate-spin mx-auto mb-4" />
<h2 className="text-2xl font-bold text-apple-gray-900 mb-2">
支付处理中
</h2>
<p className="text-apple-gray-600 mb-6">
您的订单已提交,正在等待支付确认
</p>
{orderNo && (
<p className="text-xs text-apple-gray-500 font-mono mb-6">
订单号:{orderNo}
</p>
)}
<p className="text-sm text-apple-gray-500">
如果已完成支付,请稍候片刻,系统会自动更新订单状态
</p>
</div>
)}
{orderStatus === 'failed' && (
<div className="text-center py-8">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<XCircle className="w-8 h-8 text-red-600" />
</div>
<h2 className="text-2xl font-bold text-apple-gray-900 mb-2">
支付失败
</h2>
<p className="text-apple-gray-600 mb-6">
订单处理失败,请检查支付信息或联系客服
</p>
<div className="space-y-3">
<Link
href="/#pricing"
className="block w-full py-3 bg-gradient-to-r from-apple-blue to-blue-600 text-white rounded-full font-semibold button-hover apple-shadow"
>
重新购买
</Link>
<Link
href="/#contact"
className="block w-full py-3 border-2 border-apple-gray-200 text-apple-gray-700 rounded-full font-semibold hover:border-apple-blue hover:text-apple-blue transition-all"
>
联系客服
</Link>
</div>
</div>
)}
</div>
</div>
</div>
)
}
export default function PaymentSuccessPage() {
return (
<Suspense
fallback={
<div className="min-h-screen bg-gradient-to-b from-apple-gray-50 to-white flex items-center justify-center px-4 py-12">
<div className="text-center">
<Loader2 className="w-12 h-12 text-apple-blue animate-spin mx-auto mb-4" />
<p className="text-apple-gray-600">页面加载中...</p>
</div>
</div>
}
>
<PaymentSuccessContent />
</Suspense>
)
}