Bladeren bron

feat(payment): 新增支付结果页及H5微信支付跳转功能

- 新增支付结果页面 threePayRes,展示支付成功或失败信息
- threePay 页面增加15分钟倒计时功能,倒计时结束跳转支付结果页
- 支付成功后跳转支付结果页,传递支付金额和状态参数
- 在 user store 添加 handleCommonWechatPay 方法,实现H5环境的微信支付跳转
- pages.json 中注册 threePayRes 路由,设置页面样式和登录拦截
- 修改 confirmOrder 页面区分 H5 与小程序支付逻辑,H5 调用 handleCommonWechatPay
- 配置文件调整 H5 环境开发地址,方便本地调试支付相关功能
zhangtao 6 dagen geleden
bovenliggende
commit
8b38cd2266

+ 6 - 1
src/config/index.ts

@@ -43,7 +43,12 @@ export const StaticUrl = 'https://zswl-smqjh.oss-cn-chengdu.aliyuncs.com/static/
 function handleEnvVersion() {
   // #ifdef H5
   const mode = import.meta.env.MODE
-  return mode === 'development' ? 'http://47.109.84.152:8081' : 'https://smqjh.api.zswlgz.com'
+  const h5Server = {
+    // development: 'http://47.109.84.152:8081',
+    development: 'http://192.168.0.157:8080',
+    production: 'https://smqjh.api.zswlgz.com',
+  }
+  return h5Server[mode as 'development' | 'production']
   // #endif
   // #ifdef MP-WEIXIN
   return mapEnvVersion[uni.getAccountInfoSync().miniProgram.envVersion]

+ 10 - 0
src/pages.json

@@ -281,6 +281,16 @@
             "navigationBarTitleText": "支付"
           }
         },
+        {
+          "path": "threePayRes/index",
+          "name": "common-threePayRes",
+          "islogin": true,
+          "style": {
+            "navigationBarTitleText": "支付结果",
+            "navigationStyle": "custom",
+            "disableScroll": true
+          }
+        },
         {
           "path": "user-center/index",
           "name": "common-user-center",

+ 19 - 0
src/store/user.ts

@@ -515,5 +515,24 @@ export const useUserStore = defineStore('user', {
         useGlobalToast().show('取消订单失败')
       }
     },
+    /**
+     * h5专属。小程序不可调用
+     * h5第三方统一拉起微信支付跳转路径及参数
+     * @param orderNumber
+     */
+    handleCommonWechatPay(orderNumber: string) {
+      // #ifdef H5
+      uni.showLoading({ mask: true })
+      const query = {
+        orderNumber,
+      }
+      const queryString = Object.entries(query)
+        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
+        .join('&')
+      const path = 'subPack-common/threePay/index'
+      window.location.href = window.location.href = `weixin://dl/business/?appid=wx43b5b906cc30ed0b&path=${path}&query=${queryString}&env_version=release`
+      uni.hideLoading()
+      // #endif
+    },
   },
 })

+ 70 - 1
src/subPack-common/threePay/index.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import type { wxpay } from '@/api/globals'
+import router from '@/router'
 
 definePage({ name: 'smqjh-threePay', islogin: false, style: { navigationBarTitleText: '支付' } })
 
@@ -17,6 +18,7 @@ async function handleGoPay() {
   if (priceInfo.value?.payType !== 1) {
     try {
       await useUserStore().getWxCommonPayment(priceInfo.value as wxpay)
+      router.push({ name: 'common-threePayRes', params: { price: String(priceInfo.value?.price), type: '1' } })
     }
     catch {
       // console.log(111)
@@ -24,13 +26,80 @@ async function handleGoPay() {
     }
   }
 }
+
+// 倒计时:15分钟 = 900秒
+const COUNTDOWN_SECONDS = 15 * 60
+const countdown = ref(COUNTDOWN_SECONDS)
+const countdownTimer = ref<ReturnType<typeof setInterval> | null>(null)
+
+// 模块级变量:记录倒计时结束的时间戳,离页后回来可继续
+// 页面卸载(onUnload)时重置,保证下次进入是全新的15分钟
+let endTimestamp: number | null = null
+
+// 格式化为 MM:SS
+const countdownText = computed(() => {
+  const m = Math.floor(countdown.value / 60).toString().padStart(2, '0')
+  const s = (countdown.value % 60).toString().padStart(2, '0')
+  return `${m}:${s}`
+})
+
+function updateCountdown() {
+  if (!endTimestamp)
+    return
+  const remaining = Math.max(0, Math.ceil((endTimestamp - Date.now()) / 1000))
+  countdown.value = remaining
+  if (remaining <= 0) {
+    stopCountdown()
+    // TODO: 倒计时结束后的跳转逻辑
+    router.push({ name: 'common-threePayRes', params: { price: String(priceInfo.value?.price), type: '0' } })
+  }
+}
+
+function startCountdown() {
+  // 首次进入:初始化结束时间戳
+  if (!endTimestamp) {
+    endTimestamp = Date.now() + COUNTDOWN_SECONDS * 1000
+  }
+  // 立即同步一次剩余时间(防止回来时短暂显示旧值)
+  updateCountdown()
+  stopCountdown()
+  countdownTimer.value = setInterval(updateCountdown, 1000)
+}
+
+function stopCountdown() {
+  if (countdownTimer.value) {
+    clearInterval(countdownTimer.value)
+    countdownTimer.value = null
+  }
+}
+
+// 页面显示时启动/恢复倒计时(含首次进入和从其他页面返回)
+onShow(() => {
+  startCountdown()
+})
+
+// 离开页面时暂停定时器,但保留 endTimestamp
+onHide(() => {
+  stopCountdown()
+})
+
+// 页面卸载时清理,下次进入重新开始
+onUnload(() => {
+  stopCountdown()
+  endTimestamp = null
+})
 </script>
 
 <template>
   <view class="px20rpx pt20rpx">
-    <view class="mb20rpx text-center text-48rpx text-#FF4A39">
+    <view class="text-center text-48rpx text-#FF4A39">
       ¥{{ priceInfo?.price }}
     </view>
+    <view class="mb20rpx mt24rpx text-center text-26rpx text-[#999]">
+      支付剩余时间:<text class="text-[#FF4A39] font-semibold">
+        {{ countdownText }}
+      </text>
+    </view>
     <wd-button block @click="handleGoPay">
       确认支付
     </wd-button>

+ 67 - 0
src/subPack-common/threePayRes/index.vue

@@ -0,0 +1,67 @@
+<script setup lang="ts">
+import { StaticUrl } from '@/config'
+
+definePage({
+  name: 'common-threePayRes',
+  islogin: true,
+  style: {
+    navigationBarTitleText: '支付结果',
+    navigationStyle: 'custom',
+    disableScroll: true,
+  },
+})
+const price = ref()
+const type = ref(0) // 1支付成功,0支付失败
+onLoad((options: any) => {
+  price.value = options.price
+  type.value = options.type
+})
+function handleClose() {
+  // 关闭当前小程序
+  uni.exitMiniProgram()
+}
+</script>
+
+<template>
+  <view class="pages">
+    <wd-navbar
+      title="支付结果" custom-style="background-color:transparent !important" :bordered="false" :z-index="99"
+
+      safe-area-inset-top left-arrow fixed
+      @click-left="handleClose"
+    />
+    <view class="header-line h406rpx" />
+    <view class="-mt200rpx">
+      <view class="flex flex-col items-center">
+        <image
+          :src="`${StaticUrl}/revalue-success.png`"
+          class="h200rpx w200rpx"
+        />
+        <view class="mt20rpx text-32rpx font-semibold">
+          {{ type === 1 ? '支付成功' : '支付失败' }}
+        </view>
+        <view v-if="type == 0" class="mt20rpx text-24rpx text-#AAAAAA">
+          失败原因:支付超时
+        </view>
+        <view v-else class="mt20rpx text-24rpx text-#AAAAAA">
+          实付金额:<text class="text-32rpx text-#FF4A39">
+            ¥{{ price }}
+          </text>
+        </view>
+        <view class="mt60rpx flex items-center">
+          <wd-button block size="large" @click="handleClose">
+            <text class="text-32rpx font-semibold">
+              关闭页面
+            </text>
+          </wd-button>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style scoped>
+.header-line{
+  background: linear-gradient( 179deg, rgba(190,255,13,0.4) 0%, rgba(220,255,125,0.2) 49%, rgba(158,214,5,0) 100%);
+}
+</style>

+ 7 - 0
src/subPack-xsb/confirmOrder/index.vue

@@ -145,6 +145,7 @@ async function handlePay() {
       unref(remarks),
       confirmedCouponId.value,
     )
+    // #ifdef MP-WEIXIN
     const res = await useUserStore().handleCommonPayMent(orderNumber)
     await useUserStore().clearCart(orderInfo.value.skuList)
     totalProduct.value = null
@@ -160,6 +161,12 @@ async function handlePay() {
     else {
       await useUserStore().paySuccess('xsb-order', 'subPack-xsb/commonTab/index')
     }
+    // #endif
+    // #ifdef H5
+    useUserStore().handleCommonWechatPay(orderNumber)
+    await useUserStore().clearCart(orderInfo.value.skuList)
+    totalProduct.value = null
+    // #endif
   }
   catch {
     isPay.value = false

+ 1 - 0
src/uni-pages.d.ts

@@ -25,6 +25,7 @@ interface NavigateToOptions {
        "/subPack-common/revalue/index" |
        "/subPack-common/revalueSuccess/index" |
        "/subPack-common/threePay/index" |
+       "/subPack-common/threePayRes/index" |
        "/subPack-common/user-center/index" |
        "/subPack-smqjh/bannerDetaile/index" |
        "/subPack-smqjh/order/index" |