|
@@ -4,9 +4,9 @@
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
import Taro, { useRouter } from '@tarojs/taro'
|
|
import Taro, { useRouter } from '@tarojs/taro'
|
|
|
-import { useState } from 'react'
|
|
|
|
|
|
|
+import { useState, useEffect } from 'react'
|
|
|
import { View, Text } from '@tarojs/components'
|
|
import { View, Text } from '@tarojs/components'
|
|
|
-import { useQuery } from '@tanstack/react-query'
|
|
|
|
|
|
|
+import { useQuery, useMutation } from '@tanstack/react-query'
|
|
|
import { Button } from '@/components/ui/button'
|
|
import { Button } from '@/components/ui/button'
|
|
|
import { Navbar } from '@/components/ui/navbar'
|
|
import { Navbar } from '@/components/ui/navbar'
|
|
|
import {
|
|
import {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
PaymentRateLimiter,
|
|
PaymentRateLimiter,
|
|
|
retryPayment
|
|
retryPayment
|
|
|
} from '@/utils/payment'
|
|
} from '@/utils/payment'
|
|
|
-import { paymentClient } from '@/api'
|
|
|
|
|
|
|
+import { paymentClient, creditBalanceClient } from '@/api'
|
|
|
|
|
|
|
|
interface PaymentData {
|
|
interface PaymentData {
|
|
|
timeStamp: string
|
|
timeStamp: string
|
|
@@ -26,10 +26,25 @@ interface PaymentData {
|
|
|
paySign: string
|
|
paySign: string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+interface CreditBalanceData {
|
|
|
|
|
+ totalLimit: number
|
|
|
|
|
+ usedAmount: number
|
|
|
|
|
+ availableAmount: number
|
|
|
|
|
+ isEnabled: boolean
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+enum PaymentMethod {
|
|
|
|
|
+ WECHAT = 'wechat',
|
|
|
|
|
+ CREDIT = 'credit'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const PaymentPage = () => {
|
|
const PaymentPage = () => {
|
|
|
const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.PENDING)
|
|
const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.PENDING)
|
|
|
const [isProcessing, setIsProcessing] = useState(false)
|
|
const [isProcessing, setIsProcessing] = useState(false)
|
|
|
const [errorMessage, setErrorMessage] = useState('')
|
|
const [errorMessage, setErrorMessage] = useState('')
|
|
|
|
|
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod>(PaymentMethod.WECHAT)
|
|
|
|
|
+ const [creditBalance, setCreditBalance] = useState<CreditBalanceData | null>(null)
|
|
|
|
|
+ const [isCreditBalanceLoading, setIsCreditBalanceLoading] = useState(false)
|
|
|
|
|
|
|
|
// 使用useRouter钩子获取路由参数
|
|
// 使用useRouter钩子获取路由参数
|
|
|
const router = useRouter()
|
|
const router = useRouter()
|
|
@@ -38,11 +53,59 @@ const PaymentPage = () => {
|
|
|
const amount = routerParams?.amount ? parseFloat(routerParams.amount) : 0
|
|
const amount = routerParams?.amount ? parseFloat(routerParams.amount) : 0
|
|
|
const orderNo = routerParams?.orderNo
|
|
const orderNo = routerParams?.orderNo
|
|
|
|
|
|
|
|
|
|
+ // 获取用户额度信息
|
|
|
|
|
+ const fetchCreditBalance = async () => {
|
|
|
|
|
+ setIsCreditBalanceLoading(true)
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await creditBalanceClient.me.$get({})
|
|
|
|
|
+
|
|
|
|
|
+ if (response.status === 200) {
|
|
|
|
|
+ const balanceData = await response.json()
|
|
|
|
|
+ setCreditBalance(balanceData)
|
|
|
|
|
+ } else if (response.status === 404) {
|
|
|
|
|
+ // 用户没有额度记录,创建默认额度
|
|
|
|
|
+ setCreditBalance({
|
|
|
|
|
+ totalLimit: 0,
|
|
|
|
|
+ usedAmount: 0,
|
|
|
|
|
+ availableAmount: 0,
|
|
|
|
|
+ isEnabled: false
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 其他错误情况
|
|
|
|
|
+ setCreditBalance({
|
|
|
|
|
+ totalLimit: 0,
|
|
|
|
|
+ usedAmount: 0,
|
|
|
|
|
+ availableAmount: 0,
|
|
|
|
|
+ isEnabled: false
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取用户额度失败:', error)
|
|
|
|
|
+ setCreditBalance({
|
|
|
|
|
+ totalLimit: 0,
|
|
|
|
|
+ usedAmount: 0,
|
|
|
|
|
+ availableAmount: 0,
|
|
|
|
|
+ isEnabled: false
|
|
|
|
|
+ })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setIsCreditBalanceLoading(false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- // 获取支付参数
|
|
|
|
|
- const { data: paymentData, isLoading: paymentLoading } = useQuery({
|
|
|
|
|
- queryKey: ['payment-params', orderId],
|
|
|
|
|
- queryFn: async () => {
|
|
|
|
|
|
|
+ // 组件加载时获取额度信息
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ fetchCreditBalance()
|
|
|
|
|
+ }, [])
|
|
|
|
|
+
|
|
|
|
|
+ // 获取微信支付参数(手动触发)
|
|
|
|
|
+ const {
|
|
|
|
|
+ mutateAsync: fetchWechatPaymentParams,
|
|
|
|
|
+ data: paymentData,
|
|
|
|
|
+ isLoading: paymentLoading,
|
|
|
|
|
+ error: paymentError
|
|
|
|
|
+ } = useMutation({
|
|
|
|
|
+ mutationFn: async () => {
|
|
|
if (!orderId) throw new Error('订单ID无效')
|
|
if (!orderId) throw new Error('订单ID无效')
|
|
|
|
|
|
|
|
// 调用后端API获取微信支付参数
|
|
// 调用后端API获取微信支付参数
|
|
@@ -70,18 +133,81 @@ const PaymentPage = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return paymentData
|
|
return paymentData
|
|
|
- },
|
|
|
|
|
- enabled: !!orderId && paymentStatus === PaymentStatus.PENDING
|
|
|
|
|
|
|
+ }
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- // 支付状态管理
|
|
|
|
|
- const paymentStateManager = PaymentStateManager.getInstance()
|
|
|
|
|
- const rateLimiter = PaymentRateLimiter.getInstance()
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // 支付状态管理
|
|
|
|
|
+ const paymentStateManager = PaymentStateManager.getInstance()
|
|
|
|
|
+ const rateLimiter = PaymentRateLimiter.getInstance()
|
|
|
|
|
+
|
|
|
|
|
+ // 处理额度支付
|
|
|
|
|
+ const handleCreditPayment = async () => {
|
|
|
|
|
+ if (!orderId) {
|
|
|
|
|
+ setErrorMessage('订单信息不完整')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!creditBalance?.isEnabled) {
|
|
|
|
|
+ setErrorMessage('额度支付未启用')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查额度是否足够(使用订单金额检查)
|
|
|
|
|
+ if (creditBalance.availableAmount < amount) {
|
|
|
|
|
+ setErrorMessage(`额度不足,可用额度: ¥${creditBalance.availableAmount.toFixed(2)}`)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ setIsProcessing(true)
|
|
|
|
|
+ setErrorMessage('')
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.PROCESSING)
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await creditBalanceClient.payment.$post({
|
|
|
|
|
+ json: {
|
|
|
|
|
+ referenceId: orderId.toString(), // 传递订单ID而不是订单号
|
|
|
|
|
+ remark: `订单支付 - ${orderNo || `ORD${orderId}`}`
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ if (response.status === 200) {
|
|
|
|
|
+ // 额度支付成功
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.SUCCESS)
|
|
|
|
|
+
|
|
|
|
|
+ // 更新本地额度信息
|
|
|
|
|
+ const updatedBalance = await response.json()
|
|
|
|
|
+ setCreditBalance(updatedBalance)
|
|
|
|
|
+
|
|
|
|
|
+ // 跳转到支付成功页面
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ Taro.redirectTo({
|
|
|
|
|
+ url: `/pages/payment-success/index?orderId=${orderId}&amount=${amount}&paymentMethod=credit`
|
|
|
|
|
+ })
|
|
|
|
|
+ }, 1500)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const errorData = await response.json()
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.FAILED)
|
|
|
|
|
+ setErrorMessage(errorData.message || '额度支付失败')
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error('额度支付处理异常:', error)
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.FAILED)
|
|
|
|
|
+ setErrorMessage(error.message || '额度支付异常')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setIsProcessing(false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 处理支付
|
|
// 处理支付
|
|
|
const handlePayment = async () => {
|
|
const handlePayment = async () => {
|
|
|
- if (!paymentData || !orderId) {
|
|
|
|
|
- setErrorMessage('支付参数不完整')
|
|
|
|
|
|
|
+ if (selectedPaymentMethod === PaymentMethod.CREDIT) {
|
|
|
|
|
+ await handleCreditPayment()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 微信支付逻辑
|
|
|
|
|
+ if (!orderId) {
|
|
|
|
|
+ setErrorMessage('订单信息不完整')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -95,83 +221,44 @@ const PaymentPage = () => {
|
|
|
setIsProcessing(true)
|
|
setIsProcessing(true)
|
|
|
setErrorMessage('')
|
|
setErrorMessage('')
|
|
|
setPaymentStatus(PaymentStatus.PROCESSING)
|
|
setPaymentStatus(PaymentStatus.PROCESSING)
|
|
|
- paymentStateManager.setPaymentState(orderId, PaymentStatus.PROCESSING)
|
|
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // 先获取微信支付参数
|
|
|
|
|
+ const wechatPaymentData = await fetchWechatPaymentParams()
|
|
|
|
|
+
|
|
|
|
|
+ if (!wechatPaymentData) {
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.FAILED)
|
|
|
|
|
+ setErrorMessage('获取支付参数失败')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ paymentStateManager.setPaymentState(orderId, PaymentStatus.PROCESSING)
|
|
|
|
|
+
|
|
|
// 记录支付尝试
|
|
// 记录支付尝试
|
|
|
rateLimiter.recordAttempt(orderId)
|
|
rateLimiter.recordAttempt(orderId)
|
|
|
|
|
|
|
|
// 调用微信支付
|
|
// 调用微信支付
|
|
|
- // console.debug('开始调用微信支付...')
|
|
|
|
|
- const paymentResult = await requestWechatPayment(paymentData)
|
|
|
|
|
- // console.debug('微信支付调用完成,结果:', paymentResult)
|
|
|
|
|
|
|
+ const paymentResult = await requestWechatPayment(wechatPaymentData)
|
|
|
|
|
|
|
|
if (paymentResult.success) {
|
|
if (paymentResult.success) {
|
|
|
// 支付成功
|
|
// 支付成功
|
|
|
- // console.debug('微信支付成功,开始更新状态')
|
|
|
|
|
- // console.debug('更新前支付状态:', paymentStatus)
|
|
|
|
|
-
|
|
|
|
|
- // 使用函数式更新确保状态正确设置
|
|
|
|
|
- setPaymentStatus(prevStatus => {
|
|
|
|
|
- console.debug('setPaymentStatus 被调用,前一个状态:', prevStatus, '新状态:', PaymentStatus.SUCCESS)
|
|
|
|
|
- return PaymentStatus.SUCCESS
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.SUCCESS)
|
|
|
paymentStateManager.setPaymentState(orderId, PaymentStatus.SUCCESS)
|
|
paymentStateManager.setPaymentState(orderId, PaymentStatus.SUCCESS)
|
|
|
|
|
|
|
|
// 清除频率限制记录
|
|
// 清除频率限制记录
|
|
|
rateLimiter.clearAttempts(orderId)
|
|
rateLimiter.clearAttempts(orderId)
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- // 调用后端API更新订单支付状态为已支付 (2)
|
|
|
|
|
- try {
|
|
|
|
|
- // console.debug('开始调用后端API更新订单支付状态')
|
|
|
|
|
- await paymentClient.payment.updateOrderStatus.$post({
|
|
|
|
|
- json: {
|
|
|
|
|
- orderId: orderId,
|
|
|
|
|
- payState: 2 // 2表示支付成功
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- // console.debug('数据库订单支付状态更新成功')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('数据库更新订单支付状态失败:', error)
|
|
|
|
|
- // 这里不抛出错误,因为支付已经成功,只是状态同步失败
|
|
|
|
|
- // 可以记录日志或发送通知给管理员
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // console.debug('支付状态已更新为SUCCESS,准备跳转')
|
|
|
|
|
-
|
|
|
|
|
- // 强制触发UI重新渲染
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- // console.debug('强制触发UI更新')
|
|
|
|
|
- setPaymentStatus(currentStatus => currentStatus)
|
|
|
|
|
- }, 100)
|
|
|
|
|
-
|
|
|
|
|
- // 确保状态更新显示在UI上
|
|
|
|
|
|
|
+ // 跳转到支付成功页面
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
- // console.debug('开始跳转到支付成功页面')
|
|
|
|
|
Taro.redirectTo({
|
|
Taro.redirectTo({
|
|
|
- url: `/pages/payment-success/index?orderId=${orderId}&amount=${amount}`
|
|
|
|
|
|
|
+ url: `/pages/payment-success/index?orderId=${orderId}&amount=${amount}&paymentMethod=wechat`
|
|
|
})
|
|
})
|
|
|
- }, 2000) // 增加延迟时间,确保用户看到成功状态
|
|
|
|
|
|
|
+ }, 1500)
|
|
|
} else {
|
|
} else {
|
|
|
// 支付失败
|
|
// 支付失败
|
|
|
- console.debug('支付失败,开始更新状态')
|
|
|
|
|
- console.debug('更新前支付状态:', paymentStatus)
|
|
|
|
|
-
|
|
|
|
|
- setPaymentStatus(prevStatus => {
|
|
|
|
|
- console.debug('setPaymentStatus 被调用,前一个状态:', prevStatus, '新状态:', PaymentStatus.FAILED)
|
|
|
|
|
- return PaymentStatus.FAILED
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.FAILED)
|
|
|
paymentStateManager.setPaymentState(orderId, PaymentStatus.FAILED)
|
|
paymentStateManager.setPaymentState(orderId, PaymentStatus.FAILED)
|
|
|
|
|
|
|
|
- await paymentClient.payment.updateOrderStatus.$post({
|
|
|
|
|
- json: {
|
|
|
|
|
- orderId: orderId,
|
|
|
|
|
- payState: 3 // 3表示支付失败
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
if (paymentResult.type === 'cancel') {
|
|
if (paymentResult.type === 'cancel') {
|
|
|
setErrorMessage('用户取消支付')
|
|
setErrorMessage('用户取消支付')
|
|
|
} else {
|
|
} else {
|
|
@@ -190,47 +277,42 @@ const PaymentPage = () => {
|
|
|
|
|
|
|
|
// 重试支付
|
|
// 重试支付
|
|
|
const handleRetryPayment = async () => {
|
|
const handleRetryPayment = async () => {
|
|
|
- if (!paymentData || !orderId) return
|
|
|
|
|
|
|
+ if (selectedPaymentMethod === PaymentMethod.CREDIT) {
|
|
|
|
|
+ await handleCreditPayment()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!orderId) return
|
|
|
|
|
|
|
|
setIsProcessing(true)
|
|
setIsProcessing(true)
|
|
|
setErrorMessage('')
|
|
setErrorMessage('')
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // 先获取微信支付参数
|
|
|
|
|
+ const wechatPaymentData = await fetchWechatPaymentParams()
|
|
|
|
|
+
|
|
|
|
|
+ if (!wechatPaymentData) {
|
|
|
|
|
+ setPaymentStatus(PaymentStatus.FAILED)
|
|
|
|
|
+ setErrorMessage('获取支付参数失败')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
const retryResult = await retryPayment(
|
|
const retryResult = await retryPayment(
|
|
|
- () => requestWechatPayment(paymentData),
|
|
|
|
|
|
|
+ () => requestWechatPayment(wechatPaymentData),
|
|
|
3,
|
|
3,
|
|
|
1000
|
|
1000
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
if (retryResult.success) {
|
|
if (retryResult.success) {
|
|
|
- console.debug('重试支付成功,开始更新状态')
|
|
|
|
|
setPaymentStatus(PaymentStatus.SUCCESS)
|
|
setPaymentStatus(PaymentStatus.SUCCESS)
|
|
|
paymentStateManager.setPaymentState(orderId, PaymentStatus.SUCCESS)
|
|
paymentStateManager.setPaymentState(orderId, PaymentStatus.SUCCESS)
|
|
|
|
|
|
|
|
- // 调用后端API更新订单支付状态为已支付 (2)
|
|
|
|
|
- try {
|
|
|
|
|
- console.debug('开始调用后端API更新订单支付状态(重试)')
|
|
|
|
|
- await paymentClient.payment.updateOrderStatus.$post({
|
|
|
|
|
- json: {
|
|
|
|
|
- orderId: orderId,
|
|
|
|
|
- payState: 2 // 2表示支付成功
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- console.debug('订单支付状态更新成功(重试)')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('更新订单支付状态失败(重试):', error)
|
|
|
|
|
- // 这里不抛出错误,因为支付已经成功,只是状态同步失败
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- console.debug('重试支付状态已更新为SUCCESS,准备跳转')
|
|
|
|
|
-
|
|
|
|
|
// 跳转到支付成功页面
|
|
// 跳转到支付成功页面
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
- console.debug('开始跳转到支付成功页面(重试)')
|
|
|
|
|
Taro.redirectTo({
|
|
Taro.redirectTo({
|
|
|
- url: `/pages/payment-success/index?orderId=${orderId}&amount=${amount}`
|
|
|
|
|
|
|
+ url: `/pages/payment-success/index?orderId=${orderId}&amount=${amount}&paymentMethod=wechat`
|
|
|
})
|
|
})
|
|
|
- }, 2000) // 增加延迟时间,确保用户看到成功状态
|
|
|
|
|
|
|
+ }, 1500)
|
|
|
} else {
|
|
} else {
|
|
|
setPaymentStatus(PaymentStatus.FAILED)
|
|
setPaymentStatus(PaymentStatus.FAILED)
|
|
|
setErrorMessage(retryResult.message || '支付重试失败')
|
|
setErrorMessage(retryResult.message || '支付重试失败')
|
|
@@ -314,19 +396,89 @@ const PaymentPage = () => {
|
|
|
<View className="p-5">
|
|
<View className="p-5">
|
|
|
{/* 头部 */}
|
|
{/* 头部 */}
|
|
|
<View className="text-center py-6 bg-white rounded-2xl mb-5">
|
|
<View className="text-center py-6 bg-white rounded-2xl mb-5">
|
|
|
- <Text className="text-2xl font-bold text-gray-800">支付订单</Text>
|
|
|
|
|
|
|
+ <Text className="text-2xl font-bold text-gray-800" data-testid="payment-page-title">支付订单</Text>
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
{/* 订单信息 */}
|
|
{/* 订单信息 */}
|
|
|
- <View className="bg-white rounded-2xl p-6 mb-5">
|
|
|
|
|
|
|
+ <View className="bg-white rounded-2xl p-6 mb-5" data-testid="order-info">
|
|
|
<View className="flex justify-between items-center mb-4">
|
|
<View className="flex justify-between items-center mb-4">
|
|
|
<Text className="text-sm text-gray-600">订单号:</Text>
|
|
<Text className="text-sm text-gray-600">订单号:</Text>
|
|
|
- <Text className="text-sm text-gray-800">{orderNo || `ORD${orderId}`}</Text>
|
|
|
|
|
|
|
+ <Text className="text-sm text-gray-800" data-testid="order-no">{orderNo || `ORD${orderId}`}</Text>
|
|
|
</View>
|
|
</View>
|
|
|
<View className="flex justify-between items-center">
|
|
<View className="flex justify-between items-center">
|
|
|
<Text className="text-sm text-gray-600">支付金额:</Text>
|
|
<Text className="text-sm text-gray-600">支付金额:</Text>
|
|
|
- <Text className="text-2xl font-bold text-orange-500">¥{amount.toFixed(2)}</Text>
|
|
|
|
|
|
|
+ <Text className="text-2xl font-bold text-orange-500" data-testid="payment-amount">¥{amount.toFixed(2)}</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 支付方式选择 */}
|
|
|
|
|
+ <View className="bg-white rounded-2xl p-6 mb-5">
|
|
|
|
|
+ <Text className="text-sm font-bold text-gray-800 block mb-4">选择支付方式</Text>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 微信支付选项 */}
|
|
|
|
|
+ <View
|
|
|
|
|
+ className={`flex items-center justify-between p-4 mb-3 rounded-xl border-2 ${
|
|
|
|
|
+ selectedPaymentMethod === PaymentMethod.WECHAT
|
|
|
|
|
+ ? 'border-blue-500 bg-blue-50'
|
|
|
|
|
+ : 'border-gray-200'
|
|
|
|
|
+ }`}
|
|
|
|
|
+ onClick={() => setSelectedPaymentMethod(PaymentMethod.WECHAT)}
|
|
|
|
|
+ data-testid="wechat-payment-option"
|
|
|
|
|
+ >
|
|
|
|
|
+ <View className="flex items-center">
|
|
|
|
|
+ <View className="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center mr-3">
|
|
|
|
|
+ <Text className="text-green-600 text-lg">💰</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View>
|
|
|
|
|
+ <Text className="text-sm font-bold text-gray-800">微信支付</Text>
|
|
|
|
|
+ <Text className="text-xs text-gray-500">使用微信支付完成付款</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ {selectedPaymentMethod === PaymentMethod.WECHAT && (
|
|
|
|
|
+ <View className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center" data-testid="wechat-selected">
|
|
|
|
|
+ <Text className="text-white text-xs">✓</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 额度支付选项 - 只在额度满足时才显示 */}
|
|
|
|
|
+ {creditBalance?.isEnabled && creditBalance?.availableAmount >= amount && (
|
|
|
|
|
+ <View
|
|
|
|
|
+ className={`flex items-center justify-between p-4 rounded-xl border-2 ${
|
|
|
|
|
+ selectedPaymentMethod === PaymentMethod.CREDIT
|
|
|
|
|
+ ? 'border-blue-500 bg-blue-50'
|
|
|
|
|
+ : 'border-gray-200'
|
|
|
|
|
+ }`}
|
|
|
|
|
+ onClick={() => setSelectedPaymentMethod(PaymentMethod.CREDIT)}
|
|
|
|
|
+ data-testid="credit-payment-option"
|
|
|
|
|
+ >
|
|
|
|
|
+ <View className="flex items-center">
|
|
|
|
|
+ <View className="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center mr-3">
|
|
|
|
|
+ <Text className="text-purple-600 text-lg">💳</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View>
|
|
|
|
|
+ <Text className="text-sm font-bold text-gray-800">额度支付</Text>
|
|
|
|
|
+ <Text className="text-xs text-gray-500" data-testid="available-amount-text">
|
|
|
|
|
+ 使用信用额度支付
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ {selectedPaymentMethod === PaymentMethod.CREDIT && (
|
|
|
|
|
+ <View className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center" data-testid="credit-selected">
|
|
|
|
|
+ <Text className="text-white text-xs">✓</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ {/* 额度支付说明 */}
|
|
|
|
|
+ {selectedPaymentMethod === PaymentMethod.CREDIT && creditBalance && (
|
|
|
|
|
+ <View className="mt-4 p-3 bg-blue-50 rounded-lg" data-testid="credit-payment-details">
|
|
|
|
|
+ <Text className="text-xs text-blue-700">
|
|
|
|
|
+ • 使用信用额度支付,无需立即付款
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
{/* 支付状态 */}
|
|
{/* 支付状态 */}
|
|
@@ -339,12 +491,21 @@ const PaymentPage = () => {
|
|
|
{paymentStatus === PaymentStatus.PENDING && (
|
|
{paymentStatus === PaymentStatus.PENDING && (
|
|
|
<Button
|
|
<Button
|
|
|
onClick={handlePayment}
|
|
onClick={handlePayment}
|
|
|
- disabled={isProcessing || paymentLoading}
|
|
|
|
|
- className={`w-full h-22 bg-gradient-to-r from-orange-500 to-orange-400 text-white rounded-full text-lg font-bold ${
|
|
|
|
|
|
|
+ disabled={isProcessing || paymentLoading || (selectedPaymentMethod === PaymentMethod.CREDIT && (!creditBalance?.isEnabled || creditBalance?.availableAmount < amount))}
|
|
|
|
|
+ className={`w-full h-22 ${
|
|
|
|
|
+ selectedPaymentMethod === PaymentMethod.CREDIT
|
|
|
|
|
+ ? 'bg-gradient-to-r from-purple-500 to-purple-400'
|
|
|
|
|
+ : 'bg-gradient-to-r from-orange-500 to-orange-400'
|
|
|
|
|
+ } text-white rounded-full text-lg font-bold ${
|
|
|
isProcessing ? 'bg-gray-400' : ''
|
|
isProcessing ? 'bg-gray-400' : ''
|
|
|
}`}
|
|
}`}
|
|
|
|
|
+ data-testid="pay-button"
|
|
|
>
|
|
>
|
|
|
- {isProcessing ? '支付中...' : `确认支付 ¥${amount.toFixed(2)}`}
|
|
|
|
|
|
|
+ {isProcessing ? '支付中...' :
|
|
|
|
|
+ selectedPaymentMethod === PaymentMethod.CREDIT
|
|
|
|
|
+ ? `额度支付 ¥${amount.toFixed(2)}`
|
|
|
|
|
+ : `微信支付 ¥${amount.toFixed(2)}`
|
|
|
|
|
+ }
|
|
|
</Button>
|
|
</Button>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
@@ -366,8 +527,8 @@ const PaymentPage = () => {
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
{paymentStatus === PaymentStatus.SUCCESS && (
|
|
{paymentStatus === PaymentStatus.SUCCESS && (
|
|
|
- <Button disabled className="w-full h-22 bg-green-500 text-white rounded-full text-lg font-bold">
|
|
|
|
|
- ✓ 支付成功
|
|
|
|
|
|
|
+ <Button disabled className="w-full h-22 bg-gray-100 text-gray-500 rounded-full text-sm">
|
|
|
|
|
+ 支付成功
|
|
|
</Button>
|
|
</Button>
|
|
|
)}
|
|
)}
|
|
|
</View>
|
|
</View>
|