293 lines
12 KiB
TypeScript
293 lines
12 KiB
TypeScript
'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>
|
||
)
|
||
}
|
||
|