update pay ui

This commit is contained in:
2025-12-12 14:04:11 +08:00
parent 2a4e2a41ec
commit 39c6eacb58

View File

@@ -36,7 +36,9 @@ import {
FaTimes,
FaChevronDown,
FaChevronUp,
FaExternalLinkAlt,
} from 'react-icons/fa';
import { AlipayCircleOutlined } from '@ant-design/icons';
import { logger } from '../../utils/logger';
import { useAuth } from '../../contexts/AuthContext';
@@ -147,6 +149,7 @@ export default function SubscriptionContentNew() {
const [paymentCountdown, setPaymentCountdown] = useState(300);
const [autoCheckInterval, setAutoCheckInterval] = useState(null);
const [forceUpdating, setForceUpdating] = useState(false);
const [paymentMethod, setPaymentMethod] = useState<'wechat' | 'alipay'>('wechat'); // 支付方式
const [openFaqIndex, setOpenFaqIndex] = useState(null);
@@ -392,14 +395,21 @@ export default function SubscriptionContentNew() {
}
}
const paymentMethodName = paymentMethod === 'alipay' ? 'alipay' : 'wechat_pay';
subscriptionEvents.trackPaymentInitiated({
planName: selectedPlan.name,
paymentMethod: 'wechat_pay',
paymentMethod: paymentMethodName,
amount: price,
billingCycle: selectedCycle,
});
const response = await fetch('/api/payment/create-order', {
// 根据支付方式选择不同的 API
const apiUrl = paymentMethod === 'alipay'
? '/api/payment/alipay/create-order'
: '/api/payment/create-order';
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -415,28 +425,54 @@ export default function SubscriptionContentNew() {
if (response.ok) {
const data = await response.json();
if (data.success) {
setPaymentOrder(data.data);
setPaymentCountdown(30 * 60); // 30分钟
startAutoPaymentCheck(data.data.id);
if (paymentMethod === 'alipay') {
// 支付宝:跳转到支付页面
if (data.data.pay_url) {
setPaymentOrder(data.data);
setPaymentCountdown(30 * 60);
startAutoPaymentCheck(data.data.id, 'alipay');
toast({
title: '订单创建成功',
description: '请使用微信扫描二维码完成支付',
status: 'success',
duration: 3000,
isClosable: true,
});
toast({
title: '订单创建成功',
description: '正在跳转到支付宝支付页面...',
status: 'success',
duration: 3000,
isClosable: true,
});
// 延迟跳转,让用户看到提示
setTimeout(() => {
window.open(data.data.pay_url, '_blank');
}, 500);
} else {
throw new Error('支付链接获取失败');
}
} else {
// 微信:显示二维码
setPaymentOrder(data.data);
setPaymentCountdown(30 * 60);
startAutoPaymentCheck(data.data.id, 'wechat');
toast({
title: '订单创建成功',
description: '请使用微信扫描二维码完成支付',
status: 'success',
duration: 3000,
isClosable: true,
});
}
} else {
throw new Error(data.message || '创建订单失败');
throw new Error(data.error || data.message || '创建订单失败');
}
} else {
throw new Error('网络请求失败');
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || '网络请求失败');
}
} catch (error: any) {
subscriptionEvents.trackPaymentFailed(
{
planName: selectedPlan.name,
paymentMethod: 'wechat_pay',
paymentMethod: paymentMethod === 'alipay' ? 'alipay' : 'wechat_pay',
amount: getCurrentPrice(selectedPlan),
},
error.message
@@ -454,25 +490,30 @@ export default function SubscriptionContentNew() {
}
};
const startAutoPaymentCheck = (orderId: string) => {
const startAutoPaymentCheck = (orderId: string, method: 'wechat' | 'alipay' = 'wechat') => {
// 根据支付方式选择不同的状态查询 API
const statusApiUrl = method === 'alipay'
? `/api/payment/alipay/order/${orderId}/status`
: `/api/payment/order/${orderId}/status`;
const checkInterval = setInterval(async () => {
try {
const response = await fetch(`/api/payment/order/${orderId}/status`, {
const response = await fetch(statusApiUrl, {
credentials: 'include',
});
if (response.ok) {
const data = await response.json();
if (data.success && data.data.status === 'paid') {
if (data.success && (data.data.status === 'paid' || data.payment_success)) {
clearInterval(checkInterval);
setAutoCheckInterval(null);
subscriptionEvents.trackPaymentSuccessful({
planName: selectedPlan?.name,
paymentMethod: 'wechat_pay',
paymentMethod: method === 'alipay' ? 'alipay' : 'wechat_pay',
amount: paymentOrder?.amount,
orderId: orderId,
transactionId: data.data.transaction_id,
transactionId: data.data.transaction_id || data.data.alipay_trade_no,
});
toast({
@@ -500,13 +541,19 @@ export default function SubscriptionContentNew() {
setForceUpdating(true);
try {
const response = await fetch(`/api/payment/order/${paymentOrder.order_id}/status`, {
// 根据订单的支付方式选择不同的查询 API
const orderPaymentMethod = (paymentOrder as any).payment_method || paymentMethod;
const statusApiUrl = orderPaymentMethod === 'alipay'
? `/api/payment/alipay/order/${(paymentOrder as any).id}/status`
: `/api/payment/order/${(paymentOrder as any).id}/status`;
const response = await fetch(statusApiUrl, {
credentials: 'include',
});
if (response.ok) {
const data = await response.json();
if (data.success && data.data.status === 'paid') {
if (data.success && (data.data.status === 'paid' || data.payment_success)) {
toast({
title: '支付成功!',
description: '您的订阅已激活',
@@ -540,6 +587,13 @@ export default function SubscriptionContentNew() {
}
};
// 重新打开支付宝支付页面
const handleReopenAlipay = () => {
if (paymentOrder && (paymentOrder as any).pay_url) {
window.open((paymentOrder as any).pay_url, '_blank');
}
};
// 合并数据库数据和前端配置
const getMergedPlans = () => {
// 如果数据库还没有加载数据,使用静态配置
@@ -1176,11 +1230,60 @@ export default function SubscriptionContentNew() {
<Modal isOpen={isOpen} onClose={onClose} size="lg" isCentered>
<ModalOverlay bg="rgba(0, 0, 0, 0.8)" backdropFilter="blur(10px)" />
<ModalContent bg="#1e1e1e" borderRadius="2xl" border="1px solid rgba(255, 255, 255, 0.1)">
<ModalHeader color="white"></ModalHeader>
<ModalHeader color="white">
{paymentMethod === 'alipay' ? '支付宝支付' : '微信支付'}
</ModalHeader>
<ModalCloseButton color="white" />
<ModalBody pb={6}>
{!paymentOrder ? (
<VStack spacing={6} align="stretch">
{/* 支付方式选择 */}
<Box>
<Text color="rgba(255, 255, 255, 0.7)" fontSize="sm" mb={3}>
</Text>
<HStack spacing={3}>
<Button
flex={1}
h="60px"
bg={paymentMethod === 'wechat' ? 'rgba(7, 193, 96, 0.15)' : 'rgba(255, 255, 255, 0.05)'}
border="2px solid"
borderColor={paymentMethod === 'wechat' ? '#07C160' : 'rgba(255, 255, 255, 0.1)'}
borderRadius="xl"
onClick={() => setPaymentMethod('wechat')}
_hover={{
borderColor: paymentMethod === 'wechat' ? '#07C160' : 'rgba(255, 255, 255, 0.3)',
bg: paymentMethod === 'wechat' ? 'rgba(7, 193, 96, 0.2)' : 'rgba(255, 255, 255, 0.08)',
}}
transition="all 0.2s"
>
<HStack spacing={3}>
<Icon as={FaWeixin} color="#07C160" boxSize={6} />
<Text color="white" fontWeight="medium"></Text>
</HStack>
</Button>
<Button
flex={1}
h="60px"
bg={paymentMethod === 'alipay' ? 'rgba(22, 119, 255, 0.15)' : 'rgba(255, 255, 255, 0.05)'}
border="2px solid"
borderColor={paymentMethod === 'alipay' ? '#1677FF' : 'rgba(255, 255, 255, 0.1)'}
borderRadius="xl"
onClick={() => setPaymentMethod('alipay')}
_hover={{
borderColor: paymentMethod === 'alipay' ? '#1677FF' : 'rgba(255, 255, 255, 0.3)',
bg: paymentMethod === 'alipay' ? 'rgba(22, 119, 255, 0.2)' : 'rgba(255, 255, 255, 0.08)',
}}
transition="all 0.2s"
>
<HStack spacing={3}>
<Box as={AlipayCircleOutlined} fontSize="24px" color="#1677FF" />
<Text color="white" fontWeight="medium"></Text>
</HStack>
</Button>
</HStack>
</Box>
{/* 订阅类型提示 */}
{selectedPlan && priceInfo && (
<>
@@ -1357,26 +1460,67 @@ export default function SubscriptionContentNew() {
<Button
w="full"
size="lg"
bgGradient="linear-gradient(135deg, #D4AF37, #B8941F)"
color="#000"
bgGradient={paymentMethod === 'alipay'
? 'linear-gradient(135deg, #1677FF, #0958d9)'
: 'linear-gradient(135deg, #07C160, #059048)'}
color="white"
fontWeight="bold"
onClick={handleCreatePaymentOrder}
isLoading={loading}
isDisabled={!selectedPlan}
leftIcon={paymentMethod === 'alipay'
? <Box as={AlipayCircleOutlined} fontSize="20px" />
: <Icon as={FaWeixin} boxSize={5} />}
_hover={{
bgGradient: 'linear-gradient(135deg, #F4E3A7, #D4AF37)',
bgGradient: paymentMethod === 'alipay'
? 'linear-gradient(135deg, #4096ff, #1677FF)'
: 'linear-gradient(135deg, #10d76e, #07C160)',
transform: 'translateY(-2px)',
boxShadow: paymentMethod === 'alipay'
? '0 8px 25px rgba(22, 119, 255, 0.4)'
: '0 8px 25px rgba(7, 193, 96, 0.4)',
}}
transition="all 0.3s"
>
{priceInfo?.is_upgrade && priceInfo?.final_amount === 0
? '立即免费升级'
: '创建微信支付订单'}
: paymentMethod === 'alipay'
? '支付宝支付'
: '微信扫码支付'}
</Button>
</VStack>
) : (
<VStack spacing={4}>
<Text color="rgba(255, 255, 255, 0.7)" fontSize="lg" fontWeight="bold">
使
</Text>
{/* 根据支付方式显示不同提示 */}
{(paymentOrder as any).payment_method === 'alipay' ? (
<>
<Box
p={4}
bg="rgba(22, 119, 255, 0.1)"
borderRadius="lg"
border="1px solid rgba(22, 119, 255, 0.3)"
w="full"
textAlign="center"
>
<HStack justify="center" spacing={2} mb={2}>
<Box as={AlipayCircleOutlined} fontSize="24px" color="#1677FF" />
<Text color="#1677FF" fontSize="lg" fontWeight="bold">
</Text>
</HStack>
<Text color="rgba(255, 255, 255, 0.7)" fontSize="sm">
</Text>
<Text color="rgba(255, 255, 255, 0.5)" fontSize="xs" mt={1}>
</Text>
</Box>
</>
) : (
<Text color="rgba(255, 255, 255, 0.7)" fontSize="lg" fontWeight="bold">
使
</Text>
)}
{/* 倒计时 */}
<Box
@@ -1389,7 +1533,7 @@ export default function SubscriptionContentNew() {
<HStack justify="center" spacing={2} mb={2}>
<Icon as={FaClock} color="orange.400" />
<Text color="orange.300" fontSize="sm">
: {formatTime(paymentCountdown)}
: {formatTime(paymentCountdown)}
</Text>
</HStack>
<Progress
@@ -1400,36 +1544,38 @@ export default function SubscriptionContentNew() {
/>
</Box>
{/* 二维码 */}
<Box textAlign="center">
{paymentOrder.qr_code_url ? (
<Image
src={paymentOrder.qr_code_url}
alt="微信支付二维码"
mx="auto"
maxW="200px"
border="2px solid"
borderColor="rgba(255, 255, 255, 0.2)"
borderRadius="lg"
bg="white"
p={2}
/>
) : (
<Flex
w="200px"
h="200px"
mx="auto"
bg="rgba(255, 255, 255, 0.05)"
alignItems="center"
justifyContent="center"
border="2px solid"
borderColor="rgba(255, 255, 255, 0.2)"
borderRadius="lg"
>
<Icon as={FaQrcode} color="rgba(255, 255, 255, 0.3)" boxSize={12} />
</Flex>
)}
</Box>
{/* 微信二维码(仅微信支付显示) */}
{(paymentOrder as any).payment_method !== 'alipay' && (
<Box textAlign="center">
{(paymentOrder as any).qr_code_url ? (
<Image
src={(paymentOrder as any).qr_code_url}
alt="微信支付二维码"
mx="auto"
maxW="200px"
border="2px solid"
borderColor="rgba(255, 255, 255, 0.2)"
borderRadius="lg"
bg="white"
p={2}
/>
) : (
<Flex
w="200px"
h="200px"
mx="auto"
bg="rgba(255, 255, 255, 0.05)"
alignItems="center"
justifyContent="center"
border="2px solid"
borderColor="rgba(255, 255, 255, 0.2)"
borderRadius="lg"
>
<Icon as={FaQrcode} color="rgba(255, 255, 255, 0.3)" boxSize={12} />
</Flex>
)}
</Box>
)}
{/* 订单信息 */}
<Box
@@ -1440,18 +1586,39 @@ export default function SubscriptionContentNew() {
w="full"
>
<Text fontSize="xs" color="rgba(255, 255, 255, 0.5)" mb={2}>
: {paymentOrder.order_no}
: {(paymentOrder as any).order_no}
</Text>
<Flex justify="space-between" align="baseline">
<Text color="rgba(255, 255, 255, 0.7)">:</Text>
<Text fontSize="xl" fontWeight="bold" color="#D4AF37">
¥{paymentOrder.amount}
¥{(paymentOrder as any).amount}
</Text>
</Flex>
</Box>
{/* 操作按钮 */}
<VStack spacing={3} w="full">
{/* 支付宝:重新打开支付页面按钮 */}
{(paymentOrder as any).payment_method === 'alipay' && (paymentOrder as any).pay_url && (
<Button
w="full"
size="lg"
bgGradient="linear-gradient(135deg, #1677FF, #0958d9)"
color="white"
fontWeight="bold"
leftIcon={<Icon as={FaExternalLinkAlt} />}
onClick={handleReopenAlipay}
_hover={{
bgGradient: 'linear-gradient(135deg, #4096ff, #1677FF)',
transform: 'translateY(-2px)',
boxShadow: '0 8px 25px rgba(22, 119, 255, 0.4)',
}}
transition="all 0.3s"
>
</Button>
)}
<Button
w="full"
size="lg"