fuyuan-housekeeping-uniapp/src/components/payment/payment.vue

526 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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订单idfrom订单来源
*/
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>