diff --git a/src/actions/resource.ts b/src/actions/resource.ts index 378e0d5..3e53bf7 100644 --- a/src/actions/resource.ts +++ b/src/actions/resource.ts @@ -94,3 +94,10 @@ export async function completeResource(props: { }) { return callByUser('/api/trade/complete', props) } + +export async function payClose(props: { + trade_no: string + method: number +}) { + return callByUser('/api/trade/cancel', props) +} diff --git a/src/components/composites/payment/desktop-payment.tsx b/src/components/composites/payment/desktop-payment.tsx index 5c0df20..959167e 100644 --- a/src/components/composites/payment/desktop-payment.tsx +++ b/src/components/composites/payment/desktop-payment.tsx @@ -1,5 +1,5 @@ 'use client' -import {DialogContent, DialogHeader, DialogTitle} from '@/components/ui/dialog' +import {DialogClose, DialogContent, DialogHeader, DialogTitle} from '@/components/ui/dialog' import {Button} from '@/components/ui/button' import {Loader} from 'lucide-react' import {useState} from 'react' @@ -11,7 +11,7 @@ export function DesktopPayment(props: PaymentModalProps) { const onSubmit = async () => { setLoading(true) - await props.onConfirm?.() + await props.onConfirm(true) setLoading(false) } @@ -67,9 +67,11 @@ export function DesktopPayment(props: PaymentModalProps) { {loading && } 已完成支付 - + + + diff --git a/src/components/composites/payment/mobile-payment.tsx b/src/components/composites/payment/mobile-payment.tsx index 64d77ef..a29b57f 100644 --- a/src/components/composites/payment/mobile-payment.tsx +++ b/src/components/composites/payment/mobile-payment.tsx @@ -1,5 +1,5 @@ 'use client' -import {DialogContent} from '@/components/ui/dialog' +import {DialogClose, DialogContent} from '@/components/ui/dialog' import {Button} from '@/components/ui/button' import {toast} from 'sonner' import {CreditCard, Loader} from 'lucide-react' @@ -26,7 +26,7 @@ export function MobilePayment(props: PaymentModalProps) { // 处理支付完成确认 const handlePaymentComplete = async () => { setLoading(true) - await props.onConfirm?.() // 调用父组件传入的确认方法 + await props.onConfirm(true) // 调用父组件传入的确认方法 setLoading(false) } @@ -77,13 +77,15 @@ export function MobilePayment(props: PaymentModalProps) {
{!paymentInitiated ? ( // 未发起支付时显示 <> - + + + - - {trade && ( - - )} - - ) -} diff --git a/src/components/composites/payment/payment-modal.tsx b/src/components/composites/payment/payment-modal.tsx index eaa11e6..8fe0e28 100644 --- a/src/components/composites/payment/payment-modal.tsx +++ b/src/components/composites/payment/payment-modal.tsx @@ -4,26 +4,75 @@ import {DesktopPayment} from './desktop-payment' import {TradePlatform} from '@/lib/models/trade' import {Dialog} from '@/components/ui/dialog' import {PaymentProps} from './type' +import {payClose} from '@/actions/resource' +import {useEffect} from 'react' +import {useRouter} from 'next/navigation' export type PaymentModalProps = { - onConfirm?: () => Promise - onClose?: () => void + onConfirm: (showFail: boolean) => Promise + onClose: () => void } & PaymentProps export function PaymentModal(props: PaymentModalProps) { + // 手动关闭时的处理 + const handleClose = async () => { + try { + const req = { + trade_no: props.inner_no, + method: props.method, + } + const res = await payClose(req) + } + catch (error) { + console.error('关闭订单失败:', error) + } + finally { + props.onClose?.() + } + } + + // 轮询检查支付状态 + useEffect(() => { + const pollInterval = 2000 + const maxRetries = 30 + let retries = 0 + + const interval = setInterval(async () => { + try { + await props.onConfirm(false) + return + } + catch (error) { + console.error('支付状态检查失败:', error) + } + finally { + retries++ + if (retries >= maxRetries) { + clearInterval(interval) + } + } + }, pollInterval) + + return () => { + clearInterval(interval) + } + }, [props]) + return ( { - if (!open) props.onClose?.() + if (!open) handleClose() }}> {props.platform === TradePlatform.Mobile ? ( ) : ( )} diff --git a/src/components/composites/purchase/pay.tsx b/src/components/composites/purchase/pay.tsx index 22f8017..8449b2f 100644 --- a/src/components/composites/purchase/pay.tsx +++ b/src/components/composites/purchase/pay.tsx @@ -1,5 +1,5 @@ 'use client' -import {Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle} from '@/components/ui/dialog' +import {Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle} from '@/components/ui/dialog' import {Button} from '@/components/ui/button' import balance from './_assets/balance.svg' import Image from 'next/image' @@ -39,13 +39,14 @@ export default function Pay(props: PayProps) { const method = props.method === 'alipay' ? TradeMethod.SftAlipay : TradeMethod.SftWechat - const res = { + const req = { ...props.resource, payment_method: method, payment_platform: platform, } - const resp = await prepareResource(res) + const resp = await prepareResource(req) + if (!resp.success) { toast.error(`创建订单失败: ${resp.message}`) setOpen(false) @@ -62,10 +63,9 @@ export default function Pay(props: PayProps) { }) } - const onSubmit = async () => { + const purchase = async (showFail: boolean) => { try { let resp: Awaited> | Awaited> - if (props.method === 'balance') { resp = await createResource(props.resource) } @@ -79,22 +79,26 @@ export default function Pay(props: PayProps) { throw new Error('支付信息不存在') } - if (!resp.success) throw new Error(resp.message) + if (!resp.success) { + throw new Error(resp.message) + } + setOpen(false) + setTrade(null) toast.success('购买成功', { action: { label: '去提取', onClick: () => router.push('/admin/extract'), }, }) - - setOpen(false) - await refreshProfile() + refreshProfile() } catch (e) { - toast.error('购买失败', { - description: (e as Error).message, - }) + if (showFail) { + toast.error('购买失败', { + description: (e as Error).message, + }) + } } } @@ -164,38 +168,25 @@ export default function Pay(props: PayProps) { - + + + )} - {/* 支付宝/微信支付使用公共组件 */} + {/* 支付宝/微信支付 */} {props.method !== 'balance' && trade && ( { - try { - const resp = await completeResource({trade_no: trade.inner_no, method: trade.method}) - if (!resp.success) { - throw new Error(resp.message) - } - - toast.success('支付成功') - setTrade(null) - setOpen(false) - refreshProfile() - } - catch (e) { - toast.error('支付验证失败', {description: (e as Error).message}) - } - }} + onConfirm={purchase} onClose={() => { setTrade(null) setOpen(false) diff --git a/src/components/composites/recharge/index.tsx b/src/components/composites/recharge/index.tsx index c8be651..167e93e 100644 --- a/src/components/composites/recharge/index.tsx +++ b/src/components/composites/recharge/index.tsx @@ -91,7 +91,7 @@ export default function RechargeModal(props: RechargeModelProps) { } } - const handlePaymentSuccess = async () => { + const handlePaymentSuccess = async (showFail: boolean) => { if (!trade) return try { const resp = await RechargeComplete({ @@ -110,7 +110,9 @@ export default function RechargeModal(props: RechargeModelProps) { await refreshProfile() } catch (e) { - toast.error('支付验证失败', {description: (e as Error).message}) + if (showFail) { + toast.error('支付验证失败', {description: (e as Error).message}) + } } }