WechatPayment.vue 6.9 KB
<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>