完善微信支付二维码生成机制

This commit is contained in:
2025-05-09 16:37:05 +08:00
parent 0665a4e13c
commit ea211e85a9
3 changed files with 88 additions and 81 deletions

View File

@@ -5,7 +5,7 @@ import alipay from '../_assets/alipay.svg'
import wechat from '../_assets/wechat.svg' import wechat from '../_assets/wechat.svg'
import balance from '../_assets/balance.svg' import balance from '../_assets/balance.svg'
import Image from 'next/image' import Image from 'next/image'
import {useContext, useRef, useState} from 'react' import {useContext, useEffect, useRef, useState} from 'react'
import {StoreContext, useProfileStore} from '@/components/providers/StoreProvider' import {StoreContext, useProfileStore} from '@/components/providers/StoreProvider'
import {Alert, AlertDescription} from '@/components/ui/alert' import {Alert, AlertDescription} from '@/components/ui/alert'
import { import {
@@ -38,6 +38,14 @@ export default function Pay(props: PayProps) {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [payInfo, setPayInfo] = useState<CreateResourceResp | undefined>() const [payInfo, setPayInfo] = useState<CreateResourceResp | undefined>()
const canvas = useRef<HTMLCanvasElement>(null) const canvas = useRef<HTMLCanvasElement>(null)
useEffect(() => {
if (canvas.current && payInfo) {
qrcode.toCanvas(canvas.current, payInfo.pay_url, {
width: 200,
margin: 0,
})
}
}, [payInfo])
const onOpen = async () => { const onOpen = async () => {
setOpen(true) setOpen(true)
@@ -48,12 +56,12 @@ export default function Pay(props: PayProps) {
let resp: ApiResponse<CreateResourceResp> let resp: ApiResponse<CreateResourceResp>
switch (props.method) { switch (props.method) {
case 'alipay': case 'alipay':
resp = await prepareResourceByAlipay(props.resource) resp = await prepareResourceByAlipay(props.resource)
break break
case 'wechat': case 'wechat':
resp = await prepareResourceByWechat(props.resource) resp = await prepareResourceByWechat(props.resource)
break break
} }
if (!resp.success) { if (!resp.success) {
toast.error(`创建订单失败: ${resp.message}`) toast.error(`创建订单失败: ${resp.message}`)
@@ -62,7 +70,6 @@ export default function Pay(props: PayProps) {
} }
setPayInfo(resp.data) setPayInfo(resp.data)
await qrcode.toCanvas(canvas.current, resp.data.pay_url)
} }
const router = useRouter() const router = useRouter()
@@ -70,19 +77,19 @@ export default function Pay(props: PayProps) {
let resp: ApiResponse let resp: ApiResponse
try { try {
switch (props.method) { switch (props.method) {
case 'alipay': case 'alipay':
resp = await createResourceByAlipay({ resp = await createResourceByAlipay({
trade_no: payInfo!.trade_no, trade_no: payInfo!.trade_no,
}) })
break break
case 'wechat': case 'wechat':
resp = await createResourceByWechat({ resp = await createResourceByWechat({
trade_no: payInfo!.trade_no, trade_no: payInfo!.trade_no,
}) })
break break
case 'balance': case 'balance':
resp = await createResourceByBalance(props.resource) resp = await createResourceByBalance(props.resource)
break break
} }
if (!resp.success) { if (!resp.success) {
@@ -175,14 +182,10 @@ export default function Pay(props: PayProps) {
) : ( ) : (
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<div className="bg-gray-100 w-52 h-52 flex items-center justify-center"> <div className="bg-gray-100 size-50 flex items-center justify-center">
{payInfo ? ( {payInfo ? (
props.method === 'alipay' props.method === 'alipay'
? <iframe ? <iframe src={payInfo.pay_url} className="w-full h-full" />
src={payInfo.pay_url}
className="w-full h-full"
title="支付二维码"
/>
: <canvas ref={canvas} className="w-full h-full"/> : <canvas ref={canvas} className="w-full h-full"/>
) : ( ) : (
<Loader size={40} className={`animate-spin text-weak`}/> <Loader size={40} className={`animate-spin text-weak`}/>

View File

@@ -17,7 +17,7 @@ import {zodResolver} from '@hookform/resolvers/zod'
import {toast} from 'sonner' import {toast} from 'sonner'
import wechat from '@/components/composites/purchase/_assets/wechat.svg' import wechat from '@/components/composites/purchase/_assets/wechat.svg'
import alipay from '@/components/composites/purchase/_assets/alipay.svg' import alipay from '@/components/composites/purchase/_assets/alipay.svg'
import {useRef, useState} from 'react' import {useEffect, useRef, useState} from 'react'
import {Loader} from 'lucide-react' import {Loader} from 'lucide-react'
import {RechargeByAlipay, RechargeByAlipayConfirm, RechargeByWechat, RechargeByWechatConfirm} from '@/actions/user' import {RechargeByAlipay, RechargeByAlipayConfirm, RechargeByWechat, RechargeByWechatConfirm} from '@/actions/user'
import * as qrcode from 'qrcode' import * as qrcode from 'qrcode'
@@ -52,50 +52,60 @@ export default function RechargeModal(props: RechargeModelProps) {
const method = form.watch('method') const method = form.watch('method')
const amount = form.watch('amount') const amount = form.watch('amount')
const canvas = useRef<HTMLCanvasElement>(null) const [step, setStep] = useState(0)
const [payInfo, setPayInfo] = useState<{ const [payInfo, setPayInfo] = useState<{
trade_no: string trade_no: string
pay_url: string pay_url: string
}>() }>()
const canvas = useRef<HTMLCanvasElement>(null)
useEffect(() => {
if (canvas.current && payInfo) {
qrcode.toCanvas(canvas.current, payInfo.pay_url, {
width: 200,
margin: 0,
})
}
}, [payInfo])
const refreshProfile = useProfileStore(store => store.refreshProfile) const refreshProfile = useProfileStore(store => store.refreshProfile)
const createRecharge = async (data: Schema) => { const createRecharge = async (data: Schema) => {
try { try {
switch (data.method) { switch (data.method) {
case 'alipay': case 'alipay':
const aliRes = await RechargeByAlipay({ const aliRes = await RechargeByAlipay({
amount: data.amount, amount: data.amount,
})
if (aliRes.success) {
setStep(1)
setPayInfo(aliRes.data)
}
else {
toast.error(`创建订单失败`, {
description: aliRes.message,
}) })
if (aliRes.success) { }
setStep(1) break
setPayInfo(aliRes.data) case 'wechat':
} const weRes = await RechargeByWechat({
else { amount: data.amount,
toast.error(`充值失败`, { })
description: aliRes.message, if (weRes.success) {
}) setStep(1)
} setPayInfo(weRes.data)
break }
case 'wechat': else {
const weRes = await RechargeByWechat({ toast.error(`创建订单失败`, {
amount: data.amount, description: weRes.message,
}) })
if (weRes.success) { }
setStep(1) break
setPayInfo(weRes.data)
await qrcode.toCanvas(canvas.current, weRes.data.pay_url)
}
else {
toast.error(`充值失败`, {
description: weRes.message,
})
}
break
} }
} }
catch (e) { catch (e) {
toast.error(`充值失败`, { console.error(e)
toast.error(`创建订单失败`, {
description: (e as Error).message, description: (e as Error).message,
}) })
} }
@@ -110,22 +120,22 @@ export default function RechargeModal(props: RechargeModelProps) {
} }
try { try {
switch (method) { switch (method) {
case 'alipay': case 'alipay':
const aliRes = await RechargeByAlipayConfirm({ const aliRes = await RechargeByAlipayConfirm({
trade_no: payInfo.trade_no, trade_no: payInfo.trade_no,
}) })
if (!aliRes.success) { if (!aliRes.success) {
throw new Error(aliRes.message) throw new Error(aliRes.message)
} }
break break
case 'wechat': case 'wechat':
const weRes = await RechargeByWechatConfirm({ const weRes = await RechargeByWechatConfirm({
trade_no: payInfo.trade_no, trade_no: payInfo.trade_no,
}) })
if (!weRes.success) { if (!weRes.success) {
throw new Error(weRes.message) throw new Error(weRes.message)
} }
break break
} }
toast.success(`充值成功`) toast.success(`充值成功`)
closeDialog() closeDialog()
@@ -144,7 +154,6 @@ export default function RechargeModal(props: RechargeModelProps) {
setStep(0) setStep(0)
} }
const [step, setStep] = useState(0)
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
@@ -242,14 +251,10 @@ export default function RechargeModal(props: RechargeModelProps) {
{step == 1 && <> {step == 1 && <>
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<div className="bg-gray-100 w-52 h-52 flex items-center justify-center"> <div className="bg-gray-100 size-50 flex items-center justify-center">
{payInfo ? {payInfo ?
method === 'alipay' method === 'alipay'
? <iframe ? <iframe src={payInfo.pay_url} className="w-full h-full"/>
src={payInfo.pay_url}
className="w-full h-full"
title="支付二维码"
/>
: <canvas ref={canvas} className="w-full h-full"/> : <canvas ref={canvas} className="w-full h-full"/>
: ( : (
<Loader size={40} className={`animate-spin text-weak`}/> <Loader size={40} className={`animate-spin text-weak`}/>

View File

@@ -1,6 +1,5 @@
import {NextRequest, NextResponse} from 'next/server' import {NextRequest, NextResponse} from 'next/server'
import {refreshAuth} from '@/actions/auth' import {refreshAuth} from '@/actions/auth'
import { UnauthorizedError } from './lib/api'
export const config = { export const config = {
matcher: [ matcher: [