'use client' import React, { useEffect, useState } from 'react' import { Result, Button, Spin, Card, Modal } from 'antd' import { CheckCircleOutlined, CloseCircleOutlined, LoadingOutlined } from '@ant-design/icons' import { useRouter, useSearchParams } from 'next/navigation' import { getCheckoutSessionStatus, PaymentStatusResponse } from '@/lib/stripe' /** * 支付状态枚举 */ enum PaymentStatus { LOADING = 'loading', SUCCESS = 'success', FAILED = 'failed' } /** * 支付回调模态框组件 * 处理Stripe Checkout支付完成后的状态展示和用户操作 */ export default function CallbackModal({ paymentType = 'subscription', onClose }: { paymentType?: 'subscription' | 'token'; onClose: () => void; }) { const router = useRouter() const searchParams = useSearchParams() const [paymentStatus, setPaymentStatus] = useState(PaymentStatus.LOADING) const [paymentInfo, setPaymentInfo] = useState(null) const [isModalOpen, setIsModalOpen] = useState(true) const callBackUrl = localStorage.getItem('callBackUrl') || '/' /** * 获取Stripe Checkout Session支付状态 */ const fetchPaymentStatus = async () => { try { // 从URL参数获取session_id和user_id const sessionId = searchParams.get('session_id') const userId = searchParams.get('user_id') if (!sessionId || !userId) { throw new Error('Missing required parameters: session_id or user_id') } // 调用真实的Stripe API获取支付状态 const response = await getCheckoutSessionStatus(sessionId, userId) if (response.successful && response.data) { const { payment_status, biz_order_no, pay_time, subscription } = response.data if (payment_status === 'success') { setPaymentStatus(PaymentStatus.SUCCESS) setPaymentInfo({ orderId: biz_order_no, sessionId, paymentTime: pay_time, subscription: subscription ? { planName: subscription.plan_name, planDisplayName: subscription.plan_display_name, status: subscription.status, currentPeriodEnd: subscription.current_period_end } : null }) } else if (payment_status === 'fail') { setPaymentStatus(PaymentStatus.FAILED) setPaymentInfo({ orderId: biz_order_no, sessionId, errorMessage: 'Payment processing failed, please try again' }) } else { // pending状态,继续等待 setTimeout(fetchPaymentStatus, 5000) } } else { throw new Error(response.message || 'Failed to get payment status') } } catch (error) { console.error('Failed to get payment status:', error) setPaymentStatus(PaymentStatus.FAILED) setPaymentInfo({ errorMessage: error instanceof Error ? error.message : 'Network error, unable to get payment status' }) } } // 渲染模态框内容 const renderModalContent = () => { // 加载状态 if (paymentStatus === PaymentStatus.LOADING) { return (
} size="large" />
Getting payment status...
Please wait, we are processing your payment information
) } // 支付成功状态 if (paymentStatus === PaymentStatus.SUCCESS) { return (

Payment Successful!

{paymentInfo?.subscription && ( <>

Subscription Plan: {paymentInfo.subscription.planDisplayName}

Subscription Status: {paymentInfo.subscription.status}

{paymentInfo.subscription.currentPeriodEnd && (

Expiry Date: {new Date(paymentInfo.subscription.currentPeriodEnd).toLocaleString()}

)} )} {paymentInfo?.tokenPurchase && ( <>

Token Purchase Successful!

Your credits will be updated shortly.

)} {paymentInfo?.paymentTime && (

Payment Time: {new Date(paymentInfo.paymentTime).toLocaleString()}

)}

Thank you for your purchase!

) } // 支付失败状态 return (

Payment Failed

{paymentInfo?.errorMessage || 'An error occurred during payment processing'}

If the problem persists, please contact customer service

) } const watResult = async function (ev: MessageEvent<{ type: 'payment-callback', canceled: boolean, sessionId: string, userId: string }>) { if (ev.data.type === 'payment-callback') { if (ev.data.canceled) { setPaymentStatus(PaymentStatus.FAILED) return } const userId = typeof window !== 'undefined' ? JSON.parse(localStorage.getItem('currentUser') || '{}').id : 0 try { if (paymentType === 'subscription') { // 订阅支付状态查询 const res = await getCheckoutSessionStatus(ev.data.sessionId, userId) if (res.successful && res.data) { if (res.data.payment_status === 'success') { setPaymentStatus(PaymentStatus.SUCCESS) setPaymentInfo({ orderId: res.data.biz_order_no, sessionId: ev.data.sessionId, paymentTime: res.data.pay_time, subscription: res.data.subscription }) // 通知定价页面支付成功 window.postMessage({ type: 'payment-result', success: true }, '*') } else if (res.data.payment_status === 'fail') { setPaymentStatus(PaymentStatus.FAILED) // 通知定价页面支付失败 window.postMessage({ type: 'payment-result', success: false }, '*') } } else { setPaymentStatus(PaymentStatus.FAILED) // 通知定价页面支付失败 window.postMessage({ type: 'payment-result', success: false }, '*') } } else if (paymentType === 'token') { // Token购买状态查询 const { getTokenPurchaseStatus } = await import('@/lib/stripe') const res = await getTokenPurchaseStatus(ev.data.sessionId) if (res.successful && res.data) { if (res.data.payment_status === 'success') { setPaymentStatus(PaymentStatus.SUCCESS) setPaymentInfo({ orderId: ev.data.sessionId, sessionId: ev.data.sessionId, tokenPurchase: { success: true } }) // Token购买成功后,延迟刷新页面以更新积分显示 setTimeout(() => window.location.reload(), 2000) } else if (res.data.payment_status === 'fail') { setPaymentStatus(PaymentStatus.FAILED) } } else { setPaymentStatus(PaymentStatus.FAILED) } } } catch (error) { console.error('支付状态查询失败:', error) setPaymentStatus(PaymentStatus.FAILED) } } } useEffect(() => { window.addEventListener('message', watResult) return () => { window.removeEventListener('message', watResult) } }, []) return ( } maskClosable={false} keyboard={false} footer={null} width={500} centered title={null} className="payment-callback-modal" onCancel={onClose} styles={{ content: { backgroundColor: 'black', border: '1px solid rgba(255, 255, 255, 0.2)', }, mask: { backgroundColor: 'rgba(0, 0, 0, 0.8)', }, header: { borderBottom: 'none', } }} > {renderModalContent()} ) }