修复支付取消的调用和规定时间内更新订单支付状态
This commit is contained in:
@@ -94,3 +94,10 @@ export async function completeResource(props: {
|
|||||||
}) {
|
}) {
|
||||||
return callByUser('/api/trade/complete', props)
|
return callByUser('/api/trade/complete', props)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function payClose(props: {
|
||||||
|
trade_no: string
|
||||||
|
method: number
|
||||||
|
}) {
|
||||||
|
return callByUser('/api/trade/cancel', props)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'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 {Button} from '@/components/ui/button'
|
||||||
import {Loader} from 'lucide-react'
|
import {Loader} from 'lucide-react'
|
||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
@@ -11,7 +11,7 @@ export function DesktopPayment(props: PaymentModalProps) {
|
|||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await props.onConfirm?.()
|
await props.onConfirm(true)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +67,11 @@ export function DesktopPayment(props: PaymentModalProps) {
|
|||||||
{loading && <Loader className="animate-spin mr-2"/>}
|
{loading && <Loader className="animate-spin mr-2"/>}
|
||||||
已完成支付
|
已完成支付
|
||||||
</Button>
|
</Button>
|
||||||
|
<DialogClose asChild>
|
||||||
<Button theme="outline" onClick={() => props.onClose?.()}>
|
<Button theme="outline" onClick={() => props.onClose?.()}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import {DialogContent} from '@/components/ui/dialog'
|
import {DialogClose, DialogContent} from '@/components/ui/dialog'
|
||||||
import {Button} from '@/components/ui/button'
|
import {Button} from '@/components/ui/button'
|
||||||
import {toast} from 'sonner'
|
import {toast} from 'sonner'
|
||||||
import {CreditCard, Loader} from 'lucide-react'
|
import {CreditCard, Loader} from 'lucide-react'
|
||||||
@@ -26,7 +26,7 @@ export function MobilePayment(props: PaymentModalProps) {
|
|||||||
// 处理支付完成确认
|
// 处理支付完成确认
|
||||||
const handlePaymentComplete = async () => {
|
const handlePaymentComplete = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await props.onConfirm?.() // 调用父组件传入的确认方法
|
await props.onConfirm(true) // 调用父组件传入的确认方法
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +77,7 @@ export function MobilePayment(props: PaymentModalProps) {
|
|||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
{!paymentInitiated ? ( // 未发起支付时显示
|
{!paymentInitiated ? ( // 未发起支付时显示
|
||||||
<>
|
<>
|
||||||
|
<DialogClose asChild>
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
className="flex-1 py-3 text-base"
|
className="flex-1 py-3 text-base"
|
||||||
@@ -84,6 +85,7 @@ export function MobilePayment(props: PaymentModalProps) {
|
|||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
<Button
|
<Button
|
||||||
className="flex-1 py-3 text-base"
|
className="flex-1 py-3 text-base"
|
||||||
onClick={handleConfirmPayment}
|
onClick={handleConfirmPayment}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import {Button} from '@/components/ui/button'
|
|
||||||
import {useState} from 'react'
|
|
||||||
import {PaymentModal} from './payment-modal'
|
|
||||||
import {PaymentProps} from './type'
|
|
||||||
|
|
||||||
export function PaymentButton({
|
|
||||||
onClick,
|
|
||||||
disabled,
|
|
||||||
onSuccess,
|
|
||||||
}: {
|
|
||||||
onClick: () => Promise<PaymentProps>
|
|
||||||
disabled?: boolean
|
|
||||||
onSuccess?: () => void
|
|
||||||
}) {
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [trade, setTrade] = useState<PaymentProps>()
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
setLoading(true)
|
|
||||||
try {
|
|
||||||
setTrade(await onClick())
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
onClick={handleClick}
|
|
||||||
disabled={disabled || loading}
|
|
||||||
>
|
|
||||||
{loading ? '处理中...' : '立即支付'}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{trade && (
|
|
||||||
<PaymentModal
|
|
||||||
{...trade}
|
|
||||||
onConfirm={onSuccess}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -4,26 +4,75 @@ import {DesktopPayment} from './desktop-payment'
|
|||||||
import {TradePlatform} from '@/lib/models/trade'
|
import {TradePlatform} from '@/lib/models/trade'
|
||||||
import {Dialog} from '@/components/ui/dialog'
|
import {Dialog} from '@/components/ui/dialog'
|
||||||
import {PaymentProps} from './type'
|
import {PaymentProps} from './type'
|
||||||
|
import {payClose} from '@/actions/resource'
|
||||||
|
import {useEffect} from 'react'
|
||||||
|
import {useRouter} from 'next/navigation'
|
||||||
|
|
||||||
export type PaymentModalProps = {
|
export type PaymentModalProps = {
|
||||||
onConfirm?: () => Promise<void>
|
onConfirm: (showFail: boolean) => Promise<void>
|
||||||
onClose?: () => void
|
onClose: () => void
|
||||||
} & PaymentProps
|
} & PaymentProps
|
||||||
|
|
||||||
export function PaymentModal(props: PaymentModalProps) {
|
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 (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
defaultOpen={true}
|
defaultOpen={true}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (!open) props.onClose?.()
|
if (!open) handleClose()
|
||||||
}}>
|
}}>
|
||||||
{props.platform === TradePlatform.Mobile ? (
|
{props.platform === TradePlatform.Mobile ? (
|
||||||
<MobilePayment
|
<MobilePayment
|
||||||
{...props}
|
{...props}
|
||||||
|
onClose={handleClose}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DesktopPayment
|
<DesktopPayment
|
||||||
{...props}
|
{...props}
|
||||||
|
onClose={handleClose}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'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 {Button} from '@/components/ui/button'
|
||||||
import balance from './_assets/balance.svg'
|
import balance from './_assets/balance.svg'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
@@ -39,13 +39,14 @@ export default function Pay(props: PayProps) {
|
|||||||
const method = props.method === 'alipay'
|
const method = props.method === 'alipay'
|
||||||
? TradeMethod.SftAlipay
|
? TradeMethod.SftAlipay
|
||||||
: TradeMethod.SftWechat
|
: TradeMethod.SftWechat
|
||||||
const res = {
|
const req = {
|
||||||
...props.resource,
|
...props.resource,
|
||||||
payment_method: method,
|
payment_method: method,
|
||||||
payment_platform: platform,
|
payment_platform: platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = await prepareResource(res)
|
const resp = await prepareResource(req)
|
||||||
|
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
toast.error(`创建订单失败: ${resp.message}`)
|
toast.error(`创建订单失败: ${resp.message}`)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
@@ -62,10 +63,9 @@ export default function Pay(props: PayProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const purchase = async (showFail: boolean) => {
|
||||||
try {
|
try {
|
||||||
let resp: Awaited<ReturnType<typeof completeResource>> | Awaited<ReturnType<typeof createResource>>
|
let resp: Awaited<ReturnType<typeof completeResource>> | Awaited<ReturnType<typeof createResource>>
|
||||||
|
|
||||||
if (props.method === 'balance') {
|
if (props.method === 'balance') {
|
||||||
resp = await createResource(props.resource)
|
resp = await createResource(props.resource)
|
||||||
}
|
}
|
||||||
@@ -79,24 +79,28 @@ export default function Pay(props: PayProps) {
|
|||||||
throw new Error('支付信息不存在')
|
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('购买成功', {
|
toast.success('购买成功', {
|
||||||
action: {
|
action: {
|
||||||
label: '去提取',
|
label: '去提取',
|
||||||
onClick: () => router.push('/admin/extract'),
|
onClick: () => router.push('/admin/extract'),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
refreshProfile()
|
||||||
setOpen(false)
|
|
||||||
await refreshProfile()
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
if (showFail) {
|
||||||
toast.error('购买失败', {
|
toast.error('购买失败', {
|
||||||
description: (e as Error).message,
|
description: (e as Error).message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const balanceEnough = profile && profile.balance >= Number(props.amount)
|
const balanceEnough = profile && profile.balance >= Number(props.amount)
|
||||||
|
|
||||||
@@ -164,38 +168,25 @@ export default function Pay(props: PayProps) {
|
|||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
disabled={!balanceEnough}
|
disabled={!balanceEnough}
|
||||||
onClick={onSubmit}
|
onClick={() => purchase(true)}
|
||||||
>
|
>
|
||||||
确认支付
|
确认支付
|
||||||
</Button>
|
</Button>
|
||||||
|
<DialogClose asChild>
|
||||||
<Button theme="outline" onClick={() => setOpen(false)}>
|
<Button theme="outline" onClick={() => setOpen(false)}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 支付宝/微信支付使用公共组件 */}
|
{/* 支付宝/微信支付 */}
|
||||||
{props.method !== 'balance' && trade && (
|
{props.method !== 'balance' && trade && (
|
||||||
<PaymentModal
|
<PaymentModal
|
||||||
{...trade}
|
{...trade}
|
||||||
onConfirm={async () => {
|
onConfirm={purchase}
|
||||||
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})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setTrade(null)
|
setTrade(null)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default function RechargeModal(props: RechargeModelProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePaymentSuccess = async () => {
|
const handlePaymentSuccess = async (showFail: boolean) => {
|
||||||
if (!trade) return
|
if (!trade) return
|
||||||
try {
|
try {
|
||||||
const resp = await RechargeComplete({
|
const resp = await RechargeComplete({
|
||||||
@@ -110,9 +110,11 @@ export default function RechargeModal(props: RechargeModelProps) {
|
|||||||
await refreshProfile()
|
await refreshProfile()
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
if (showFail) {
|
||||||
toast.error('支付验证失败', {description: (e as Error).message})
|
toast.error('支付验证失败', {description: (e as Error).message})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setTrade(undefined)
|
setTrade(undefined)
|
||||||
|
|||||||
Reference in New Issue
Block a user