526 lines
17 KiB
Vue
526 lines
17 KiB
Vue
<template>
|
||
<u-popup
|
||
v-model="showPay"
|
||
mode="bottom"
|
||
safe-area-inset-bottom
|
||
:mask-close-able="false"
|
||
border-radius="14"
|
||
closeable
|
||
@close="handleClose"
|
||
>
|
||
<view class="h-[900rpx]">
|
||
<page-status :status="popupStatus" :fixed="false">
|
||
<template #error>
|
||
<u-empty text="订单信息错误,无法查询到订单信息" mode="order"></u-empty>
|
||
</template>
|
||
<template #default>
|
||
<view class="flex flex-col w-full h-full payment">
|
||
<view class="header py-[50rpx] flex flex-col items-center">
|
||
<price
|
||
:content="props.order_amount"
|
||
mainSize="44rpx"
|
||
minorSize="40rpx"
|
||
fontWeight="500"
|
||
color="#333"
|
||
></price>
|
||
|
||
<template v-if="payData.cancelTime">
|
||
<view
|
||
class="count-down flex items-center rounded-[30px] text-xs"
|
||
v-if="timeStamp >= 0"
|
||
>
|
||
<text>支付剩余时间:</text>
|
||
<u-count-down
|
||
:timestamp="timeStamp"
|
||
format="HH:mm:ss"
|
||
:font-size="22"
|
||
:separator-size="26"
|
||
@end="payData.cancelTime = 0"
|
||
/>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
<view class="main flex-1 mx-[20rpx]">
|
||
<view>
|
||
<view class="payway-lists">
|
||
<u-radio-group v-model="payWay" class="w-full">
|
||
<view
|
||
class="p-[20rpx] flex items-center w-full payway-item"
|
||
v-for="(item, index) in payData.payWayList"
|
||
:key="index"
|
||
@click="selectPayWay(item.payWay)"
|
||
>
|
||
<u-image
|
||
class="flex-none"
|
||
:src="item.payImage"
|
||
width="48"
|
||
height="48"
|
||
></u-image>
|
||
<view class="mx-[16rpx] flex-1">
|
||
<view class="payway-item--name flex-1">
|
||
{{ item.payName }}
|
||
</view>
|
||
<view
|
||
class="text-muted text-xs"
|
||
v-if="item.payWay === 2"
|
||
>
|
||
可用余额:{{ payData.money }}
|
||
</view>
|
||
</view>
|
||
|
||
<u-radio
|
||
class="mr-[-20rpx]"
|
||
:name="item.payWay"
|
||
active-color="#5ac725"
|
||
></u-radio>
|
||
</view>
|
||
</u-radio-group>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="submit-btn p-[20rpx]">
|
||
<u-button
|
||
@click="handlePay"
|
||
shape="circle"
|
||
type="primary"
|
||
:loading="isLock"
|
||
:custom-style="{ background: '#2468f2' }"
|
||
>
|
||
立即支付
|
||
</u-button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</page-status>
|
||
</view>
|
||
</u-popup>
|
||
|
||
<u-popup
|
||
class="pay-popup"
|
||
v-model="showCheckPay"
|
||
round
|
||
mode="center"
|
||
borderRadius="10"
|
||
:maskCloseAble="false"
|
||
@close="handleClose"
|
||
>
|
||
<view class="content bg-white w-[560rpx] p-[40rpx]">
|
||
<view class="text-2xl font-medium text-center">支付确认</view>
|
||
<view class="pt-[30rpx] pb-[40rpx]">
|
||
<view>请在微信内完成支付,如果您已支付成功,请点击`已完成支付`按钮</view>
|
||
</view>
|
||
<view class="flex">
|
||
<view class="flex-1 mr-[20rpx]">
|
||
<button
|
||
class="Btn bg-white text-xs text-black leading-[60rpx] h-[60rpx] rounded-full"
|
||
@click="queryPayResult(false)"
|
||
>
|
||
重新支付
|
||
</button>
|
||
</view>
|
||
<view class="flex-1">
|
||
<button
|
||
class="bg-primary text-xs ml-[20rpx] text-white leading-[60rpx] h-[60rpx] rounded-full"
|
||
@click="queryPayResult()"
|
||
>
|
||
已完成支付
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import qs from 'qs'
|
||
import { pay, PayWayEnum } from '@/utils/pay/index'
|
||
import { computed, ref, watch, watchEffect } from 'vue'
|
||
import { useLockFn } from '@/hooks/useLockFn'
|
||
import { series } from '@/utils/util'
|
||
import { ClientEnum, PageStatusEnum, PayStatusEnum } from '@/enums/appEnums'
|
||
import { useUserStore } from '@/stores/user'
|
||
import { client } from '@/utils/client'
|
||
import { getClient } from '@/utils/client'
|
||
import {
|
||
apiPayPayWay,
|
||
apiPayPrepay,
|
||
apiQueryOrder,
|
||
apiH5PayPrepay,
|
||
apiBytePayPrepay,
|
||
apiByteQueryOrder,
|
||
apiQueryRecharge
|
||
} from '@/api/app'
|
||
import price from '@/components/price/price-v2.vue'
|
||
import { apiOrderClose } from '@/api/order'
|
||
|
||
/*
|
||
页面参数 orderId:订单id,from:订单来源
|
||
*/
|
||
|
||
const props = defineProps({
|
||
show: {
|
||
type: Boolean,
|
||
required: true
|
||
},
|
||
showCheck: {
|
||
type: Boolean
|
||
},
|
||
// 订单id
|
||
orderId: {
|
||
type: Number,
|
||
required: true
|
||
},
|
||
sn: {
|
||
type: [Number, String],
|
||
required: true
|
||
},
|
||
order_amount: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
//订单来源
|
||
from: {
|
||
default: 'order',
|
||
type: String,
|
||
required: true
|
||
},
|
||
//h5微信支付回跳路径,一般为拉起支付的页面路径
|
||
redirect: {
|
||
type: String
|
||
}
|
||
})
|
||
|
||
const emit = defineEmits(['update:showCheck', 'update:show', 'close', 'success', 'fail'])
|
||
|
||
const payWay = ref()
|
||
const popupStatus = ref(PageStatusEnum.LOADING)
|
||
const payData = ref<any>({
|
||
payWayList: [],
|
||
cancelTime: null,
|
||
money: 0
|
||
})
|
||
|
||
const showCheckPay = computed({
|
||
get() {
|
||
return props.showCheck
|
||
},
|
||
set(value) {
|
||
emit('update:showCheck', value)
|
||
}
|
||
})
|
||
|
||
const showPay = computed({
|
||
get() {
|
||
return props.show
|
||
},
|
||
set(value) {
|
||
emit('update:show', value)
|
||
}
|
||
})
|
||
|
||
const handleClose = () => {
|
||
showPay.value = false
|
||
emit('close')
|
||
}
|
||
|
||
const getPayData = async () => {
|
||
popupStatus.value = PageStatusEnum.LOADING
|
||
try {
|
||
// #ifdef MP-TOUTIAO
|
||
payData.value.payWayList = [
|
||
{
|
||
id: 2,
|
||
isDefault: 2,
|
||
payId: 2,
|
||
payImage:
|
||
'http://nvqryx.natappfree.cc/api/uploads/image/20230110/8608cf8d-555d-415a-9100-c87dac00fe8e.png',
|
||
payName: '抖音支付',
|
||
payWay: 2,
|
||
scene: 2,
|
||
status: 2
|
||
}
|
||
]
|
||
popupStatus.value = PageStatusEnum.NORMAL
|
||
payWay.value = 2
|
||
// #endif
|
||
payData.value = await apiPayPayWay({
|
||
scene: getClient(),
|
||
orderId: props.orderId,
|
||
type: props.from
|
||
})
|
||
popupStatus.value = PageStatusEnum.NORMAL
|
||
const checkPay =
|
||
payData.value.payWayList.find((item: any) => item.isDefault) ||
|
||
payData.value.payWayList[0]
|
||
payWay.value = checkPay?.payWay
|
||
} catch (error) {
|
||
uni.$u.toast(error)
|
||
popupStatus.value = PageStatusEnum.ERROR
|
||
}
|
||
}
|
||
|
||
const { bindMnp } = useUserStore().userInfo as { bindMnp: boolean }
|
||
const selectPayWay = (pay: number) => {
|
||
payWay.value = pay
|
||
}
|
||
const payment = (() => {
|
||
// 查询是否绑定微信
|
||
const checkIsBindWx = async () => {
|
||
if (
|
||
[ClientEnum.OA_WEIXIN, ClientEnum.MP_WEIXIN].includes(client) &&
|
||
!bindMnp &&
|
||
payWay.value == PayWayEnum.WECHAT
|
||
) {
|
||
showPay.value = false
|
||
const res: any = await uni.showModal({
|
||
title: '温馨提示',
|
||
content: '当前账号未绑定微信,无法完成支付',
|
||
confirmText: '去绑定'
|
||
})
|
||
if (res.confirm) {
|
||
uni.navigateTo({
|
||
url: '/pages/user_set/user_set'
|
||
})
|
||
}
|
||
return Promise.reject()
|
||
}
|
||
}
|
||
|
||
// 调用预支付
|
||
const prepayTask = async () => {
|
||
uni.showLoading({
|
||
title: '正在支付中'
|
||
})
|
||
let payChannel = ''
|
||
switch (client) {
|
||
case 1:
|
||
payChannel = 'mp_channel'
|
||
break
|
||
case 2:
|
||
payChannel = 'oa_channel'
|
||
break
|
||
case 3:
|
||
payChannel = 'h5_channel'
|
||
break
|
||
default:
|
||
payChannel = 'mp_channel'
|
||
break
|
||
}
|
||
try {
|
||
const params = {
|
||
orderId: props.orderId,
|
||
payChannel,
|
||
scene: props.from,
|
||
payWay: payWay.value // 1-微信 2-余额
|
||
}
|
||
// 微信or公众号支付
|
||
if (payChannel === 'mp_channel' || payChannel === 'oa_channel') {
|
||
const res = await apiPayPrepay({ ...params })
|
||
return { pay_way: payWay.value, config: res }
|
||
} else if (payChannel === 'h5_channel') {
|
||
const res = await apiH5PayPrepay({ ...params })
|
||
const redirect = `&redirect_url=${encodeURIComponent(
|
||
window.location.href + '&checkPay=' + true + '&order_id=' + props.orderId
|
||
)}`
|
||
return { pay_way: payWay.value, config: res.url + redirect }
|
||
}
|
||
} catch (err) {
|
||
return uni.$u.toast(err)
|
||
}
|
||
}
|
||
|
||
//拉起支付
|
||
const payTask = async (data: any) => {
|
||
try {
|
||
const res = await pay.payment(data.pay_way, data.config)
|
||
console.log('resPayTask: ' + res)
|
||
|
||
return res
|
||
} catch (error) {
|
||
return Promise.reject(error)
|
||
}
|
||
}
|
||
const ttpay = async () => {}
|
||
|
||
// #ifdef MP-TOUTIAO
|
||
return series(ttpay)
|
||
// #endif
|
||
// #ifndef MP-TOUTIAO
|
||
return series(checkIsBindWx, prepayTask, payTask)
|
||
// #endif
|
||
})()
|
||
|
||
const { isLock, lockFn: handlePay } = useLockFn(async () => {
|
||
try {
|
||
// #ifdef MP-TOUTIAO
|
||
const ttpay = async () => {
|
||
const params = { order_id: props.orderId, pay_channel: 'byte_channel' }
|
||
const tempRes = await apiBytePayPrepay(params)
|
||
// debugger
|
||
console.log(tempRes.data)
|
||
console.log(tempRes?.data?.order_id)
|
||
console.log(tempRes?.data?.order_token)
|
||
let payResult: any = ''
|
||
|
||
try {
|
||
tt.pay({
|
||
orderInfo: {
|
||
order_id: tempRes.data.order_id,
|
||
order_token: tempRes.data.order_token
|
||
},
|
||
service: 5,
|
||
success(res) {
|
||
payResult = res
|
||
console.log('收到成功回调啦', res)
|
||
if (res.code === 0) {
|
||
console.log('支付成功啦~~')
|
||
const queryResult = apiByteQueryOrder({ order_id: props.orderId })
|
||
console.log(queryResult)
|
||
emit('success')
|
||
return 'success'
|
||
} else {
|
||
uni.$u.toast(res.msg)
|
||
emit('fail')
|
||
return 'fail'
|
||
}
|
||
},
|
||
fail(res) {
|
||
payResult = res
|
||
console.log('支付失败了', res)
|
||
emit('fail')
|
||
}
|
||
})
|
||
// debugger
|
||
// setTimeout(()=> {
|
||
// const res = {
|
||
// code: 200,
|
||
// data: [],
|
||
// msg: "成功"
|
||
// }
|
||
// // return Promise.resolve('success')
|
||
// emit('success')
|
||
// return "success"
|
||
// }, 1000)
|
||
} catch (e) {
|
||
console.log('ttpay catch:', e)
|
||
// Promise.reject('fail')
|
||
return 'fail'
|
||
}
|
||
}
|
||
// debugger
|
||
const res: PayStatusEnum = ttpay()
|
||
console.log('ttpay:', res)
|
||
|
||
// #endif
|
||
// #ifndef MP-TOUTIAO
|
||
const res: PayStatusEnum = await payment()
|
||
console.log('支付结果状态:', res)
|
||
handlePayResult(res)
|
||
// #endif
|
||
|
||
uni.hideLoading()
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.log(error)
|
||
}
|
||
})
|
||
|
||
const handlePayResult = async (status: PayStatusEnum) => {
|
||
await queryPayStatus()
|
||
switch (status) {
|
||
case PayStatusEnum.SUCCESS:
|
||
emit('success')
|
||
break
|
||
case PayStatusEnum.PENDING:
|
||
showCheckPay.value = true
|
||
break
|
||
case PayStatusEnum.FAIL:
|
||
emit('fail')
|
||
break
|
||
}
|
||
}
|
||
|
||
const queryPayResult = async (confirm = true) => {
|
||
const res = await queryPayStatus()
|
||
if (res.payStatus === 0) {
|
||
if (confirm == true) {
|
||
uni.$u.toast('您的订单还未支付,请重新支付')
|
||
}
|
||
showPay.value = true
|
||
// handlePayResult(PayStatusEnum.FAIL)
|
||
} else {
|
||
if (confirm == false) {
|
||
uni.$u.toast('您的订单已经支付,请勿重新支付')
|
||
}
|
||
handlePayResult(PayStatusEnum.SUCCESS)
|
||
}
|
||
showCheckPay.value = false
|
||
}
|
||
|
||
const queryPayStatus = async () => {
|
||
if (props.from === 'order' && payWay.value !== 2) {
|
||
const res = await apiQueryOrder({ id: props.orderId })
|
||
return res
|
||
} else if (props.from === 'recharge') {
|
||
await apiQueryRecharge({ id: props.orderId })
|
||
}
|
||
}
|
||
|
||
const timeStamp = ref<number | null>(0)
|
||
|
||
watchEffect(() => {
|
||
// 获取倒计时段
|
||
const endTimestamp = payData.value.cancelTime
|
||
const startTimestamp = new Date().getTime() / 1000
|
||
timeStamp.value = (endTimestamp - startTimestamp) * 1000
|
||
if (timeStamp.value === 0) orderClose()
|
||
})
|
||
// 超时关闭订单
|
||
const orderClose = async () => {
|
||
await apiOrderClose({
|
||
outTradeNo: props.orderId
|
||
})
|
||
}
|
||
|
||
watch(
|
||
() => props.show,
|
||
async value => {
|
||
if (value) {
|
||
if (!props.orderId) {
|
||
uni.$u.toast('订单信息错误,无法查询到订单信息')
|
||
return
|
||
}
|
||
getPayData()
|
||
}
|
||
},
|
||
{
|
||
immediate: true
|
||
}
|
||
)
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.payway-lists {
|
||
.payway-item {
|
||
border-bottom: 1px solid;
|
||
@apply border-page;
|
||
}
|
||
}
|
||
|
||
.Btn {
|
||
border: 1px solid rgba(187, 187, 187, 1);
|
||
background-color: rgba(255, 255, 255, 1);
|
||
color: rgba(16, 16, 16, 1);
|
||
}
|
||
|
||
.count-down {
|
||
color: #666666;
|
||
padding: 10rpx 30rpx 10rpx 30rpx;
|
||
background-color: #ffffff;
|
||
}
|
||
:deep(.u-btn--primary) {
|
||
background-color: $blue5 !important;
|
||
}
|
||
</style>
|