feat: 添加重新支付功能开关并优化支付失败处理
- 新增 ENABLE_REPAY_FEATURE 配置项(默认关闭) - 关闭重新支付时,订单列表不显示重新支付按钮和倒计时 - 支付失败/取消时友好提示并返回上一页 - 移除提交订单页的循环支付逻辑 - 优化等待页超时和失败处理,自动跳转首页 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
5 changed files
with
52 additions
and
48 deletions
| ... | @@ -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 } | ... | ... |
-
Please register or login to post a comment