WechatPayment.vue
6.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<template>
<!-- 支付成功状态 -->
<div v-if="paymentStatus === 'success'" class="h-full flex flex-col items-center justify-center px-4">
<FrostedGlass class="p-6 rounded-xl text-center">
<div class="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h2 class="text-2xl font-bold mb-2">支付成功!</h2>
<p class="text-gray-600 mb-2">您的订单已经成功提交</p>
<p class="text-gray-500 text-sm mb-6">订单号: {{ orderId }}</p>
<slot name="success-action">
<button
@click="$emit('success')"
class="w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-3 rounded-xl font-medium"
>
完成
</button>
</slot>
</FrostedGlass>
</div>
<!-- 支付处理中状态 -->
<div v-else-if="paymentStatus === 'processing'" class="h-full flex flex-col items-center justify-center px-4">
<FrostedGlass class="p-6 rounded-xl text-center">
<div class="w-20 h-20 bg-blue-50 rounded-full flex items-center justify-center mx-auto mb-4">
<div class="w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
</div>
<h2 class="text-2xl font-bold mb-2">支付处理中</h2>
<p class="text-gray-600 mb-6">请稍候,正在处理您的支付...</p>
</FrostedGlass>
</div>
<!-- 支付失败状态 -->
<div v-else-if="paymentStatus === 'failed'" class="h-full flex flex-col items-center justify-center px-4 sm:px-6 lg:px-8">
<FrostedGlass class="w-full max-w-sm sm:max-w-md lg:max-w-lg p-6 sm:p-8 rounded-2xl text-center">
<div class="w-20 h-20 sm:w-24 sm:h-24 bg-red-50 rounded-full flex items-center justify-center mx-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 sm:h-12 sm:w-12 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h2 class="text-xl sm:text-2xl font-bold mb-2 sm:mb-3">支付失败</h2>
<p class="text-sm sm:text-base text-gray-600 mb-6 sm:mb-8 px-2 sm:px-4">{{ paymentError || '支付过程中出现错误,请重试' }}</p>
<div class="space-y-2 sm:space-y-3 max-w-xs mx-auto">
<button
@click="handlePayment"
class="w-full bg-gradient-to-r from-red-500 to-red-600 text-white py-3 sm:py-3.5 rounded-xl font-medium text-base sm:text-lg hover:from-red-600 hover:to-red-700 transition-colors"
>
重新支付
</button>
<slot name="failed-action">
<button
@click="$emit('failed')"
class="w-full bg-gray-100 text-gray-700 py-3 sm:py-3.5 rounded-xl font-medium text-base sm:text-lg hover:bg-gray-200 transition-colors"
>
返回
</button>
</slot>
</div>
</FrostedGlass>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import FrostedGlass from '@/components/ui/FrostedGlass.vue'
import { wxPayAPI, wxPayCheckAPI } from "@/api/wx/pay"
// 定义组件的props
const props = defineProps({
orderId: {
type: String,
required: true
},
orderStatus: {
type: String,
required: true
}
})
// 定义组件的事件
const emit = defineEmits(['success', 'failed', 'processing'])
// 支付状态管理
const paymentStatus = ref('pending') // pending, processing, success, failed
const paymentError = ref('')
const paymentRetryCount = ref(0)
const MAX_RETRY_ATTEMPTS = 3
const RETRY_DELAY = 2000 // 2秒重试间隔
// 初始化微信支付
const initWxPay = () => {
return new Promise((resolve) => {
if (typeof WeixinJSBridge !== "undefined") {
resolve(WeixinJSBridge)
} else {
document.addEventListener('WeixinJSBridgeReady', () => {
resolve(WeixinJSBridge)
}, false)
}
})
}
// 检查支付状态
const checkPaymentStatus = async (orderId) => {
try {
const payStatus = await wxPayCheckAPI({ order_id: orderId })
if (payStatus.code) {
paymentStatus.value = 'success'
return true
}
return false
} catch (error) {
console.error('支付状态查询失败:', error)
return false
}
}
// 处理支付结果
const handlePaymentResult = async (res) => {
if (res.err_msg === "get_brand_wcpay_request:ok") {
// 支付成功,验证支付状态
let retryCount = 0
const verifyPayment = async () => {
if (await checkPaymentStatus(props.orderId)) {
emit('success')
return
}
if (retryCount < MAX_RETRY_ATTEMPTS) {
retryCount++
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY))
await verifyPayment()
} else {
paymentStatus.value = 'failed'
paymentError.value = '支付状态验证失败,请联系客服'
emit('failed', paymentError.value)
}
}
await verifyPayment()
} else if (res.err_msg === "get_brand_wcpay_request:cancel") {
paymentStatus.value = 'failed'
paymentError.value = '支付已取消'
emit('failed', paymentError.value)
} else {
paymentStatus.value = 'failed'
paymentError.value = '支付失败,请重试'
emit('failed', paymentError.value)
}
}
// 处理支付流程
const handlePayment = async () => {
try {
// 重置支付状态
paymentStatus.value = 'processing'
paymentError.value = ''
paymentRetryCount.value = 0
emit('processing')
// 初始化支付
const { code, data, msg } = await wxPayAPI({ order_id: props.orderId })
if (!code) {
throw new Error(msg || '支付初始化失败,请重试')
}
// 确保WeixinJSBridge已准备就绪
const bridge = await initWxPay()
if (!bridge) {
throw new Error('微信支付环境初始化失败,请在微信中打开')
}
// 发起支付请求
bridge.invoke('getBrandWCPayRequest', { ...data.payargs }, async (res) => {
try {
await handlePaymentResult(res)
} catch (error) {
console.error('支付处理失败:', error)
paymentStatus.value = 'failed'
paymentError.value = error.message || '支付处理失败,请重试'
emit('failed', paymentError.value)
}
})
} catch (error) {
console.error('支付失败:', error)
paymentStatus.value = 'failed'
paymentError.value = error.message || '支付失败,请重试'
emit('failed', paymentError.value)
}
}
onMounted(() => {
if (props.orderStatus === 'NOT_PAY') {
// 初始化微信支付
initWxPay()
// 开始支付流程
handlePayment()
}
if (props.orderStatus === 'PAY') {
paymentStatus.value = 'success'
emit('success')
}
})
</script>