hookehuyr

feat: 添加重新支付功能开关并优化支付失败处理

- 新增 ENABLE_REPAY_FEATURE 配置项(默认关闭)
- 关闭重新支付时,订单列表不显示重新支付按钮和倒计时
- 支付失败/取消时友好提示并返回上一页
- 移除提交订单页的循环支付逻辑
- 优化等待页超时和失败处理,自动跳转首页

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
36 >下单时间:<text>{{ reserve_info.order_time }}</text></view 36 >下单时间:<text>{{ reserve_info.order_time }}</text></view
37 > 37 >
38 </view> 38 </view>
39 - <view v-if="is_pay_pending" class="booking-list-item-footer" @tap.stop> 39 + <view v-if="enable_repay && is_pay_pending" class="booking-list-item-footer" @tap.stop>
40 <view v-if="countdown_seconds > 0" class="countdown">剩余支付时间:{{ countdown_text }}</view> 40 <view v-if="countdown_seconds > 0" class="countdown">剩余支付时间:{{ countdown_text }}</view>
41 <view v-else class="countdown timeout">支付已超时</view> 41 <view v-else class="countdown timeout">支付已超时</view>
42 <view v-if="countdown_seconds > 0" class="repay-btn" @tap.stop="onRepay">重新支付</view> 42 <view v-if="countdown_seconds > 0" class="repay-btn" @tap.stop="onRepay">重新支付</view>
...@@ -50,9 +50,13 @@ import Taro from '@tarojs/taro' ...@@ -50,9 +50,13 @@ import Taro from '@tarojs/taro'
50 import { IconFont } from '@nutui/icons-vue-taro' 50 import { IconFont } from '@nutui/icons-vue-taro'
51 import { useGo } from '@/hooks/useGo' 51 import { useGo } from '@/hooks/useGo'
52 import { wechat_pay } from '@/utils/wechatPay' 52 import { wechat_pay } from '@/utils/wechatPay'
53 +import { ENABLE_REPAY_FEATURE } from '@/utils/config'
53 54
54 const go = useGo() 55 const go = useGo()
55 56
57 +// 是否启用重新支付功能
58 +const enable_repay = computed(() => ENABLE_REPAY_FEATURE)
59 +
56 const props = defineProps({ 60 const props = defineProps({
57 data: { 61 data: {
58 type: Object, 62 type: Object,
...@@ -224,7 +228,7 @@ const onRepay = async () => { ...@@ -224,7 +228,7 @@ const onRepay = async () => {
224 while (should_continue) { 228 while (should_continue) {
225 const pay_id = reserve_info.value?.pay_id 229 const pay_id = reserve_info.value?.pay_id
226 const pay_res = await wechat_pay({ pay_id }) 230 const pay_res = await wechat_pay({ pay_id })
227 - if (pay_res && pay_res.code == 1) { 231 + if (pay_res && pay_res.code === 1) {
228 go('/success', { pay_id }) 232 go('/success', { pay_id })
229 return 233 return
230 } 234 }
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
75 import { ref, computed } from 'vue' 75 import { ref, computed } from 'vue'
76 import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro' 76 import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro'
77 import { IconFont } from '@nutui/icons-vue-taro' 77 import { IconFont } from '@nutui/icons-vue-taro'
78 -import { useGo, useReplace } from '@/hooks/useGo' 78 +import { useGo } from '@/hooks/useGo'
79 import icon_check1 from '@/assets/images/多选01@2x.png' 79 import icon_check1 from '@/assets/images/多选01@2x.png'
80 import icon_check2 from '@/assets/images/多选02@2x.png' 80 import icon_check2 from '@/assets/images/多选02@2x.png'
81 import { personListAPI, addReserveAPI } from '@/api/index' 81 import { personListAPI, addReserveAPI } from '@/api/index'
...@@ -84,7 +84,6 @@ import { mask_id_number } from '@/utils/tools' ...@@ -84,7 +84,6 @@ import { mask_id_number } from '@/utils/tools'
84 84
85 const router = useTaroRouter() 85 const router = useTaroRouter()
86 const go = useGo() 86 const go = useGo()
87 -const replace = useReplace()
88 87
89 const visitorList = ref([]) 88 const visitorList = ref([])
90 const date = ref('') 89 const date = ref('')
...@@ -174,36 +173,10 @@ const refreshVisitorList = async options => { ...@@ -174,36 +173,10 @@ const refreshVisitorList = async options => {
174 } 173 }
175 } 174 }
176 175
177 -let is_showing_pay_modal = false
178 -
179 -/**
180 - * @description 支付未完成弹窗(防并发)
181 - * @param {string} content 弹窗内容
182 - * @returns {Promise<boolean>} true=继续支付,false=离开
183 - */
184 -const showPayErrorModal = async content => {
185 - if (is_showing_pay_modal) {
186 - return
187 - }
188 - is_showing_pay_modal = true
189 - try {
190 - const res = await Taro.showModal({
191 - title: '提示',
192 - content: content || '支付失败,请稍后再试',
193 - showCancel: true,
194 - cancelText: '离开',
195 - confirmText: '继续支付'
196 - })
197 - return !!res?.confirm
198 - } finally {
199 - is_showing_pay_modal = false
200 - }
201 -}
202 -
203 /** 176 /**
204 * @description 提交订单 177 * @description 提交订单
205 - * - 先创建预约单拿 pay_id(支持“待支付订单”复用) 178 + * - 先创建预约单拿 pay_id(支持"待支付订单"复用)
206 - * - need_pay=1 时循环拉起微信支付,直到成功或用户取消 179 + * - need_pay=1 时拉起微信支付
207 * @returns {Promise<void>} 无返回值 180 * @returns {Promise<void>} 无返回值
208 */ 181 */
209 const submitBtn = async () => { 182 const submitBtn = async () => {
...@@ -236,7 +209,7 @@ const submitBtn = async () => { ...@@ -236,7 +209,7 @@ const submitBtn = async () => {
236 Taro.hideLoading() 209 Taro.hideLoading()
237 } 210 }
238 211
239 - if (!reserve_res || reserve_res.code != 1) { 212 + if (!reserve_res || reserve_res.code !== 1) {
240 return 213 return
241 } 214 }
242 pay_id = reserve_res.data.pay_id 215 pay_id = reserve_res.data.pay_id
...@@ -248,25 +221,16 @@ const submitBtn = async () => { ...@@ -248,25 +221,16 @@ const submitBtn = async () => {
248 221
249 // 以接口返回的 need_pay 为准:1=需要支付,0=不需要支付 222 // 以接口返回的 need_pay 为准:1=需要支付,0=不需要支付
250 if (Number(need_pay) === 1 || need_pay === true) { 223 if (Number(need_pay) === 1 || need_pay === true) {
251 - // 初始化循环
252 - let should_continue = true
253 - // 循环支付直到支付成功或用户取消支付
254 - while (should_continue) {
255 const pay_res = await wechat_pay({ pay_id }) 224 const pay_res = await wechat_pay({ pay_id })
256 - if (pay_res && pay_res.code == 1) { 225 + if (pay_res && pay_res.code === 1) {
257 pending_pay_id.value = null 226 pending_pay_id.value = null
258 pending_need_pay.value = null 227 pending_need_pay.value = null
259 go('/success', { pay_id }) 228 go('/success', { pay_id })
260 return 229 return
261 } 230 }
262 - // 刷新参观者列表, 清除已预约标记 231 + // 支付失败/取消后,刷新参观者列表
263 refreshVisitorList({ reset_checked: true }).catch(() => {}) 232 refreshVisitorList({ reset_checked: true }).catch(() => {})
264 - should_continue = await showPayErrorModal( 233 + // wechat_pay 内部已经处理了弹窗和返回上一页,这里不需要额外处理
265 - pay_res?.msg || '支付未完成,可再次点击提交订单继续支付'
266 - )
267 - }
268 -
269 - replace('/bookingList')
270 } else { 234 } else {
271 pending_pay_id.value = null 235 pending_pay_id.value = null
272 pending_need_pay.value = null 236 pending_need_pay.value = null
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
26 26
27 <script setup> 27 <script setup>
28 import { ref, onMounted, onUnmounted } from 'vue' 28 import { ref, onMounted, onUnmounted } from 'vue'
29 -import Taro, { useRouter } from '@tarojs/taro' 29 +import { useRouter } from '@tarojs/taro'
30 import { IconFont } from '@nutui/icons-vue-taro' 30 import { IconFont } from '@nutui/icons-vue-taro'
31 import { billPayStatusAPI } from '@/api/index' 31 import { billPayStatusAPI } from '@/api/index'
32 import { useGo } from '@/hooks/useGo' 32 import { useGo } from '@/hooks/useGo'
...@@ -59,6 +59,12 @@ const startCountdown = () => { ...@@ -59,6 +59,12 @@ const startCountdown = () => {
59 current.value.seconds = remaining.value 59 current.value.seconds = remaining.value
60 } else { 60 } else {
61 clearInterval(countdownTimer) 61 clearInterval(countdownTimer)
62 + clearInterval(timer)
63 + // 倒计时结束,跳转到首页
64 + pay_msg.value = '支付确认超时,请稍后在订单列表中查看'
65 + setTimeout(() => {
66 + go('/pages/index/index', 'replace')
67 + }, 2000)
62 } 68 }
63 }, 1000) 69 }, 1000)
64 } 70 }
...@@ -68,7 +74,7 @@ const checkStatus = async () => { ...@@ -68,7 +74,7 @@ const checkStatus = async () => {
68 return 74 return
69 } 75 }
70 try { 76 try {
71 - const { code, data } = await billPayStatusAPI({ pay_id }) 77 + const { data } = await billPayStatusAPI({ pay_id })
72 // TAG:轮询支付回调 78 // TAG:轮询支付回调
73 if (data) { 79 if (data) {
74 switch (data.status) { 80 switch (data.status) {
...@@ -85,6 +91,10 @@ const checkStatus = async () => { ...@@ -85,6 +91,10 @@ const checkStatus = async () => {
85 break 91 break
86 case PAY_STATUS.FAIL: 92 case PAY_STATUS.FAIL:
87 pay_msg.value = '订单支付失败' 93 pay_msg.value = '订单支付失败'
94 + // 支付失败后跳转到首页
95 + setTimeout(() => {
96 + go('/pages/index/index', 'replace')
97 + }, 2000)
88 break 98 break
89 } 99 }
90 } 100 }
......
...@@ -28,4 +28,11 @@ export const REQUEST_DEFAULT_PARAMS = { ...@@ -28,4 +28,11 @@ export const REQUEST_DEFAULT_PARAMS = {
28 client_name: '智慧西园寺' 28 client_name: '智慧西园寺'
29 } 29 }
30 30
31 +/**
32 + * @description 重新支付功能开关
33 + * - false:关闭重新支付功能,支付失败/取消后直接返回上一页(默认)
34 + * - true:开启重新支付功能,允许用户重新支付
35 + */
36 +export const ENABLE_REPAY_FEATURE = false
37 +
31 export default BASE_URL 38 export default BASE_URL
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
7 */ 7 */
8 import Taro from '@tarojs/taro' 8 import Taro from '@tarojs/taro'
9 import { wxPayAPI } from '@/api/wx/pay' 9 import { wxPayAPI } from '@/api/wx/pay'
10 +import { ENABLE_REPAY_FEATURE } from '@/utils/config'
10 11
11 /** 12 /**
12 * @description 微信支付 13 * @description 微信支付
...@@ -53,11 +54,29 @@ export const wechat_pay = async ({ pay_id }) => { ...@@ -53,11 +54,29 @@ export const wechat_pay = async ({ pay_id }) => {
53 // 优化错误提示文案 54 // 优化错误提示文案
54 const errMsg = pay_result?.err?.errMsg || '' 55 const errMsg = pay_result?.err?.errMsg || ''
55 let friendlyMsg = '支付未完成' 56 let friendlyMsg = '支付未完成'
57 + let showConfirmAndBack = false
56 58
57 if (errMsg.includes('cancel')) { 59 if (errMsg.includes('cancel')) {
58 friendlyMsg = '您已取消支付' 60 friendlyMsg = '您已取消支付'
61 + showConfirmAndBack = true
59 } else if (errMsg.includes('fail')) { 62 } else if (errMsg.includes('fail')) {
60 - friendlyMsg = '支付失败,请稍后重试' 63 + friendlyMsg = '支付失败'
64 + showConfirmAndBack = true
65 + }
66 +
67 + // 如果重新支付功能关闭,直接提示并返回上一页
68 + if (!ENABLE_REPAY_FEATURE && showConfirmAndBack) {
69 + await Taro.showModal({
70 + title: friendlyMsg,
71 + content: '订单未完成支付,如有需要请重新预约',
72 + showCancel: false,
73 + confirmText: '我知道了'
74 + })
75 + // 返回上一页
76 + const pages = Taro.getCurrentPages()
77 + if (pages.length > 1) {
78 + Taro.navigateBack()
79 + }
61 } 80 }
62 81
63 return { code: 0, data: pay_result?.err || null, msg: friendlyMsg } 82 return { code: 0, data: pay_result?.err || null, msg: friendlyMsg }
......