2025-04-16 18:51:17 +08:00
|
|
|
|
'use client'
|
2025-08-16 11:41:07 +08:00
|
|
|
|
import {Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle} from '@/components/ui/dialog'
|
2025-04-16 18:51:17 +08:00
|
|
|
|
import {Button} from '@/components/ui/button'
|
|
|
|
|
|
import Image from 'next/image'
|
2025-12-16 14:10:50 +08:00
|
|
|
|
import imgBalance from './_assets/balance.svg'
|
2025-06-22 14:42:21 +08:00
|
|
|
|
import {useState} from 'react'
|
2025-12-11 14:10:52 +08:00
|
|
|
|
import {useProfileStore} from '@/components/stores/profile'
|
2025-06-16 11:27:34 +08:00
|
|
|
|
import {Alert, AlertTitle} from '@/components/ui/alert'
|
2025-04-16 18:51:17 +08:00
|
|
|
|
import {toast} from 'sonner'
|
|
|
|
|
|
import {useRouter} from 'next/navigation'
|
2025-12-16 17:10:30 +08:00
|
|
|
|
import {completeResource, createResource, CreateResourceReq, prepareResource} from '@/actions/resource'
|
2025-06-23 11:20:54 +08:00
|
|
|
|
import {
|
|
|
|
|
|
TradeMethod,
|
2026-03-13 14:13:06 +08:00
|
|
|
|
TradePlatform,
|
2025-06-23 11:20:54 +08:00
|
|
|
|
} from '@/lib/models/trade'
|
2025-06-22 14:42:21 +08:00
|
|
|
|
import {PaymentModal} from '@/components/composites/payment/payment-modal'
|
2025-06-23 11:20:54 +08:00
|
|
|
|
import {PaymentProps} from '@/components/composites/payment/type'
|
2025-12-15 11:48:40 +08:00
|
|
|
|
import {usePlatformType} from '@/lib/hooks'
|
2025-04-16 18:51:17 +08:00
|
|
|
|
|
|
|
|
|
|
export type PayProps = {
|
2025-05-22 14:59:22 +08:00
|
|
|
|
amount: string
|
2025-12-16 17:10:30 +08:00
|
|
|
|
resource: CreateResourceReq
|
2025-12-11 14:10:52 +08:00
|
|
|
|
} & ({
|
|
|
|
|
|
method: 'alipay' | 'wechat'
|
|
|
|
|
|
} | {
|
|
|
|
|
|
method: 'balance'
|
|
|
|
|
|
balance: number
|
|
|
|
|
|
})
|
2025-04-16 18:51:17 +08:00
|
|
|
|
|
|
|
|
|
|
export default function Pay(props: PayProps) {
|
2025-05-19 11:04:40 +08:00
|
|
|
|
const refreshProfile = useProfileStore(store => store.refreshProfile)
|
2025-04-16 18:51:17 +08:00
|
|
|
|
const [open, setOpen] = useState(false)
|
2025-06-23 11:20:54 +08:00
|
|
|
|
const [trade, setTrade] = useState<PaymentProps | null>(null)
|
2025-06-22 14:42:21 +08:00
|
|
|
|
const router = useRouter()
|
2026-03-13 14:13:06 +08:00
|
|
|
|
// const platform = usePlatformType()
|
2025-04-16 18:51:17 +08:00
|
|
|
|
|
|
|
|
|
|
const onOpen = async () => {
|
|
|
|
|
|
setOpen(true)
|
|
|
|
|
|
|
2025-06-22 14:42:21 +08:00
|
|
|
|
if (props.method === 'balance') return
|
2025-04-16 18:51:17 +08:00
|
|
|
|
|
2025-06-25 14:43:44 +08:00
|
|
|
|
const method = props.method === 'alipay'
|
|
|
|
|
|
? TradeMethod.SftAlipay
|
|
|
|
|
|
: TradeMethod.SftWechat
|
2025-08-16 11:41:07 +08:00
|
|
|
|
const req = {
|
2025-05-22 14:59:22 +08:00
|
|
|
|
...props.resource,
|
2025-06-23 11:20:54 +08:00
|
|
|
|
payment_method: method,
|
2026-03-13 14:13:06 +08:00
|
|
|
|
payment_platform: TradePlatform.Desktop,
|
2025-06-18 19:05:38 +08:00
|
|
|
|
}
|
2026-03-30 13:11:40 +08:00
|
|
|
|
console.log(req, 'req')
|
2025-06-18 19:05:38 +08:00
|
|
|
|
|
2025-08-16 11:41:07 +08:00
|
|
|
|
const resp = await prepareResource(req)
|
|
|
|
|
|
|
2025-04-16 18:51:17 +08:00
|
|
|
|
if (!resp.success) {
|
|
|
|
|
|
toast.error(`创建订单失败: ${resp.message}`)
|
|
|
|
|
|
setOpen(false)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 14:42:21 +08:00
|
|
|
|
setTrade({
|
|
|
|
|
|
inner_no: resp.data.trade_no,
|
|
|
|
|
|
pay_url: resp.data.pay_url,
|
|
|
|
|
|
amount: Number(props.amount),
|
2026-03-13 14:13:06 +08:00
|
|
|
|
platform: TradePlatform.Desktop,
|
2025-06-23 11:20:54 +08:00
|
|
|
|
method: method,
|
2025-06-22 14:42:21 +08:00
|
|
|
|
})
|
2025-04-16 18:51:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-16 11:41:07 +08:00
|
|
|
|
const purchase = async (showFail: boolean) => {
|
2025-04-16 18:51:17 +08:00
|
|
|
|
try {
|
2025-06-22 14:42:21 +08:00
|
|
|
|
let resp: Awaited<ReturnType<typeof completeResource>> | Awaited<ReturnType<typeof createResource>>
|
|
|
|
|
|
if (props.method === 'balance') {
|
2025-05-22 14:59:22 +08:00
|
|
|
|
resp = await createResource(props.resource)
|
2025-04-16 18:51:17 +08:00
|
|
|
|
}
|
2025-06-22 14:42:21 +08:00
|
|
|
|
else if (trade) {
|
2025-06-27 10:57:50 +08:00
|
|
|
|
resp = await completeResource({
|
|
|
|
|
|
trade_no: trade.inner_no,
|
|
|
|
|
|
method: trade.method,
|
|
|
|
|
|
})
|
2025-04-16 18:51:17 +08:00
|
|
|
|
}
|
2025-06-22 14:42:21 +08:00
|
|
|
|
else {
|
|
|
|
|
|
throw new Error('支付信息不存在')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-16 11:41:07 +08:00
|
|
|
|
if (!resp.success) {
|
|
|
|
|
|
throw new Error(resp.message)
|
|
|
|
|
|
}
|
2025-04-16 18:51:17 +08:00
|
|
|
|
|
2025-08-16 11:41:07 +08:00
|
|
|
|
setOpen(false)
|
|
|
|
|
|
setTrade(null)
|
2025-04-16 18:51:17 +08:00
|
|
|
|
toast.success('购买成功', {
|
|
|
|
|
|
action: {
|
2025-06-22 14:42:21 +08:00
|
|
|
|
label: '去提取',
|
2025-04-16 18:51:17 +08:00
|
|
|
|
onClick: () => router.push('/admin/extract'),
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
2025-08-16 11:41:07 +08:00
|
|
|
|
refreshProfile()
|
2025-04-16 18:51:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (e) {
|
2025-08-16 11:41:07 +08:00
|
|
|
|
if (showFail) {
|
|
|
|
|
|
toast.error('购买失败', {
|
|
|
|
|
|
description: (e as Error).message,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-04-16 18:51:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-16 14:10:50 +08:00
|
|
|
|
const balanceEnough = props.method == 'balance' && props.balance >= Number(props.amount)
|
2025-05-22 14:59:22 +08:00
|
|
|
|
|
2025-04-16 18:51:17 +08:00
|
|
|
|
return (
|
2025-12-16 14:10:50 +08:00
|
|
|
|
<div className="mt-4">
|
|
|
|
|
|
<Button className="w-full h-12" onClick={onOpen}>
|
2025-06-22 14:42:21 +08:00
|
|
|
|
立即支付
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 余额支付对话框 */}
|
|
|
|
|
|
{props.method === 'balance' && (
|
|
|
|
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
|
|
|
|
<DialogContent>
|
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
|
<DialogTitle className="flex gap-2 items-center">
|
2025-12-16 14:10:50 +08:00
|
|
|
|
<Image src={imgBalance} alt="余额" width={20} height={20}/>
|
2025-06-07 11:49:57 +08:00
|
|
|
|
<span>余额支付</span>
|
2025-06-22 14:42:21 +08:00
|
|
|
|
</DialogTitle>
|
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
2025-12-11 14:10:52 +08:00
|
|
|
|
<div className="flex flex-col gap-4">
|
|
|
|
|
|
<div className="flex flex-col gap-1">
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<span className="text-weak text-sm">账户余额</span>
|
|
|
|
|
|
<span className="text-lg">
|
2025-12-16 14:10:50 +08:00
|
|
|
|
{props.balance} 元
|
2025-12-11 14:10:52 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<span className="text-weak text-sm">支付金额</span>
|
|
|
|
|
|
<span className="text-lg text-accent">
|
|
|
|
|
|
- {props.amount} 元
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<hr className="my-2"/>
|
|
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
|
|
<span className="text-weak text-sm">支付后余额</span>
|
|
|
|
|
|
<span className={`text-lg ${balanceEnough ? 'text-done' : 'text-fail'}`}>
|
2025-12-16 14:10:50 +08:00
|
|
|
|
{(props.balance - Number(props.amount)).toFixed(2)} 元
|
2025-12-11 14:10:52 +08:00
|
|
|
|
</span>
|
2025-04-16 18:51:17 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-11 14:10:52 +08:00
|
|
|
|
|
|
|
|
|
|
{balanceEnough ? (
|
|
|
|
|
|
<Alert variant="done">
|
|
|
|
|
|
<AlertTitle>
|
|
|
|
|
|
检查无误后,点击确认支付按钮完成支付
|
|
|
|
|
|
</AlertTitle>
|
|
|
|
|
|
</Alert>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Alert variant="fail">
|
|
|
|
|
|
<AlertTitle>
|
|
|
|
|
|
余额不足,请先充值或选择其他支付方式
|
|
|
|
|
|
</AlertTitle>
|
|
|
|
|
|
</Alert>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2025-04-16 18:51:17 +08:00
|
|
|
|
|
2025-06-22 14:42:21 +08:00
|
|
|
|
<DialogFooter>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
disabled={!balanceEnough}
|
2025-08-16 11:41:07 +08:00
|
|
|
|
onClick={() => purchase(true)}
|
2025-06-22 14:42:21 +08:00
|
|
|
|
>
|
|
|
|
|
|
确认支付
|
|
|
|
|
|
</Button>
|
2025-08-16 11:41:07 +08:00
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
|
<Button theme="outline" onClick={() => setOpen(false)}>
|
|
|
|
|
|
取消
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogClose>
|
2025-06-22 14:42:21 +08:00
|
|
|
|
</DialogFooter>
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-08-16 11:41:07 +08:00
|
|
|
|
{/* 支付宝/微信支付 */}
|
2025-06-22 14:42:21 +08:00
|
|
|
|
{props.method !== 'balance' && trade && (
|
|
|
|
|
|
<PaymentModal
|
2025-06-23 11:20:54 +08:00
|
|
|
|
{...trade}
|
2025-08-16 11:41:07 +08:00
|
|
|
|
onConfirm={purchase}
|
2025-06-23 11:20:54 +08:00
|
|
|
|
onClose={() => {
|
|
|
|
|
|
setTrade(null)
|
|
|
|
|
|
setOpen(false)
|
|
|
|
|
|
}}
|
2025-06-22 14:42:21 +08:00
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2025-12-16 14:10:50 +08:00
|
|
|
|
</div>
|
2025-04-16 18:51:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|