feat 支付订单联调

This commit is contained in:
nws
2024-01-12 00:51:24 +08:00
parent 28e43d2d45
commit 000f7d40a4
9 changed files with 201 additions and 153 deletions

View File

@@ -1,32 +0,0 @@
import { defHttp } from '/@/utils/http/axios'
/**
* 取消支付(支付id)
*/
export function cancelByPaymentId(paymentId) {
return defHttp.post({
url: '/uniPay/cancelByPaymentId',
params: { paymentId },
})
}
/**
* 退款
*/
export function refund(obj) {
return defHttp.post({
url: '/uniPay/refund',
data: obj,
})
}
/**
* 刷新指定业务id的支付单状态
*/
export function syncByBusinessId(businessId) {
return defHttp.post({
url: '/uniPay/syncByBusinessId',
method: 'POST',
params: { businessId },
})
}

View File

@@ -2,24 +2,21 @@
* 支付状态
*/
export enum PayStatus {
/** 未知状态 */
TRADE_UNKNOWN = 'trade_unknown',
/** 支付中 */
TRADE_PROGRESS = 'trade_progress',
PROGRESS = 'progress',
/** 成功 */
TRADE_SUCCESS = 'trade_success',
SUCCESS = 'success',
/** 失败 */
TRADE_FAIL = 'trade_fail',
FAIL = 'fail',
/** 支付取消(超时/手动取消/订单已经关闭撤销支付单) */
TRADE_CANCEL = 'trade_cancel',
/** 支付关闭(超时/手动取消/订单已经关闭撤销支付单) */
CANCEL = 'close',
/** 退款中(部分退款) */
TRADE_REFUNDING = 'trade_refunding',
/** 部分退款 */
PARTIAL_REFUND = 'partial_refund',
/** 退款 */
TRADE_REFUNDED = 'trade_refunded',
/** 全部退款 */
REFUNDED = 'refunded',
}

View File

@@ -9,6 +9,5 @@ export enum payChannelEnum {
WALLET = 'wallet_pay',
VOUCHER = 'voucher_pay',
CREDIT_CARD = 'credit_pay',
APPLE_PAY = 'apple_pay',
AGGREGATION = 'aggregation_pay',
}

View File

@@ -14,14 +14,53 @@ export function page(params) {
}
/**
* 获取单
* 获取
*/
export function get(id) {
export function getOrder(id) {
return defHttp.get<Result<PayOrder>>({
url: '/order/pay/findById',
params: { id },
})
}
/**
* 获取订单扩展信息
*/
export function getOrderExtra(id) {
return defHttp.get<Result<PayOrderExtra>>({
url: '/order/pay/getExtraById',
params: { id },
})
}
/**
* 获取订单关联支付通道信息
*/
export function getPayChannel(paymentId) {
return defHttp.get<Result<PayOrderChannel[]>>({
url: '/order/pay/getChannels',
params: { paymentId },
})
}
/**
* 同步支付状态
*/
export function sync(id) {
return defHttp.post<Result<PayOrder>>({
url: '/order/pay/sync',
params: { id },
})
}
/**
* 关闭支付吉利
*/
export function close(id) {
return defHttp.post<Result<PayOrder>>({
url: '/order/pay/close',
params: { id },
})
}
/**
* 支付记录
@@ -32,9 +71,9 @@ export interface PayOrder extends BaseEntity {
// 标题
title?: string
// 是否是异步支付
asyncPay: boolean
asyncPay?: boolean
// 是否是组合支付
combinationPay: boolean
combinationPay?: boolean
// 异步支付通道
asyncChannel?: boolean
// 金额
@@ -84,7 +123,7 @@ export interface PayOrderExtra {
*/
export interface PayOrderChannel {
// 支付通道
payChannel?: number
channel?: number
// 支付方式
payWay?: number
// 金额

View File

@@ -10,50 +10,71 @@
>
<a-spin :spinning="confirmLoading">
<a-descriptions title="" :column="{ md: 2, sm: 1, xs: 1 }">
<a-descriptions-item label="用户id">
{{ form.userId }}
<a-descriptions-item label="支付ID">
{{ order.id }}
</a-descriptions-item>
<a-descriptions-item label="业务号">
{{ order.businessNo }}
</a-descriptions-item>
<a-descriptions-item label="标题">
{{ form.title }}
</a-descriptions-item>
<a-descriptions-item label="业务id">
{{ form.businessId }}
</a-descriptions-item>
<a-descriptions-item label="金额">
{{ form.amount }}
</a-descriptions-item>
<a-descriptions-item label="可退余额">
{{ form.refundableBalance }}
</a-descriptions-item>
<a-descriptions-item label="支付状态">
{{ dictConvert('PayStatus', form.payStatus) }}
</a-descriptions-item>
<a-descriptions-item label="异步支付">
{{ form.asyncPayMode ? '是' : '否' }}
</a-descriptions-item>
<a-descriptions-item label="组合支付">
{{ form.combinationPayMode ? '是' : '否' }}
</a-descriptions-item>
<a-descriptions-item label="异步支付方式">
{{ dictConvert('PayChannel', form.asyncPayChannel) }}
</a-descriptions-item>
<a-descriptions-item label="支付信息">
<a-tag v-for="o in payChannelInfo" :key="o.payChannel">{{ dictConvert('PayChannel', o.payChannel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="可退款信息">
<a-tag v-for="o in form.refundableInfo" :key="o.payChannel">{{ dictConvert('PayChannel', o.payChannel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="客户IP">
{{ form.clientIp }}
{{ order.title }}
</a-descriptions-item>
<a-descriptions-item label="描述">
{{ form.description }}
{{ orderExtra.description }}
</a-descriptions-item>
<a-descriptions-item label="错误码">
{{ form.errorCode }}
<a-descriptions-item label="金额">
{{ order.amount }}
</a-descriptions-item>
<a-descriptions-item label="可退余额">
{{ order.refundableBalance }}
</a-descriptions-item>
<a-descriptions-item label="支付状态">
{{ dictConvert('PayStatus', order.payStatus) }}
</a-descriptions-item>
<a-descriptions-item label="异步支付">
{{ order.asyncPay ? '是' : '否' }}
</a-descriptions-item>
<a-descriptions-item label="组合支付">
{{ order.combinationPay ? '是' : '否' }}
</a-descriptions-item>
<a-descriptions-item label="异步支付方式">
{{ dictConvert('PayChannel', order.asyncChannel) }}
</a-descriptions-item>
<a-descriptions-item label="支付信息">
<a-tag v-for="o in orderChannel" :key="o.channel">{{ dictConvert('PayChannel', o.channel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="可退款信息">
<a-tag v-for="o in order.refundableInfo" :key="o.channel">{{ dictConvert('PayChannel', o.channel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="客户IP">
{{ orderExtra.clientIp }}
</a-descriptions-item>
<a-descriptions-item label="错误信息">
{{ form.errorMsg }}
{{ orderExtra.errorMsg }}
</a-descriptions-item>
<a-descriptions-item label="是否通知">
{{ orderExtra.notNotify ? '否' : '是' }}
</a-descriptions-item>
<a-descriptions-item label="通知地址">
{{ orderExtra.notifyUrl }}
</a-descriptions-item>
<a-descriptions-item label="签名类型">
{{ orderExtra.signType }}
</a-descriptions-item>
<a-descriptions-item label="签名">
{{ orderExtra.sign }}
</a-descriptions-item>
<a-descriptions-item label="商户扩展参数">
{{ orderExtra.attach }}
</a-descriptions-item>
<a-descriptions-item label="请求时间">
{{ orderExtra.reqTime }}
</a-descriptions-item>
<a-descriptions-item label="支付时间">
{{ order.payTime }}
</a-descriptions-item>
<a-descriptions-item label="过期时间">
{{ order.expiredTime }}
</a-descriptions-item>
</a-descriptions>
</a-spin>
@@ -68,8 +89,7 @@
<script lang="ts" setup>
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { get, PayOrder, PayOrderExtra, PayOrderChannel } from './PayOrder.api'
import { FormInstance } from 'ant-design-vue/lib/form'
import { getOrder, PayOrder, PayOrderExtra, PayOrderChannel, getOrderExtra, getPayChannel } from './PayOrder.api'
import { BasicModal } from '/@/components/Modal'
import { useDict } from '/@/hooks/bootx/useDict'
const {
@@ -88,17 +108,23 @@
} = useFormEdit()
const { dictConvert } = useDict()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<PayOrder>({})
let order = $ref<PayOrder>({})
let orderExtra = $ref<PayOrderExtra>({})
let orderChannel = $ref<PayOrderChannel[]>([])
// 入口
function init(id) {
async function init(id) {
visible.value = true
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
await getOrder(id).then(({ data }) => {
order = data
})
await getOrderExtra(id).then(({ data }) => {
orderExtra = data
})
await getPayChannel(id).then(({ data }) => {
orderChannel = data
})
confirmLoading.value = false
}
// 获取信息
defineExpose({

View File

@@ -4,52 +4,42 @@
<b-query :query-params="model.queryParam" :fields="fields" @query="queryPage" @reset="resetQueryParams" />
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }">
<template #buttons>
<b-super-query
:queryState="superQueryFlag"
:fields="queryFields"
:width="880"
button-title="超级查询器"
model-title="超级查询器"
@query="changeSuperQuery"
@reset="resetQuery"
/>
</template>
</vxe-toolbar>
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }" />
<vxe-table
row-id="id"
ref="xTable"
:sort-config="{ remote: true, trigger: 'cell', defaultSort: { field: 'createTime', order: 'desc' } }"
:sort-config="{ remote: true, trigger: 'cell' }"
:data="pagination.records"
:loading="loading"
@sort-change="sortChange"
>
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="id" title="支付ID" />
<vxe-column field="businessId" title="业务ID" />
<vxe-column field="title" title="标题" />
<vxe-column field="amount" title="金额" sortable />
<vxe-column field="refundableBalance" title="可退余额" sortable />
<vxe-column field="payStatus" title="支付状态" sortable>
<vxe-column field="amount" title="金额(分)" sortable />
<vxe-column field="refundableBalance" title="可退余额(分)" sortable />
<vxe-column field="status" title="支付状态" sortable>
<template #default="{ row }">
{{ dictConvert('PayStatus', row.payStatus) }}
<a-tag>{{ dictConvert('PayStatus', row.status) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="asyncPayMode" title="异步支付">
<vxe-column field="asyncPay" title="异步支付">
<template #default="{ row }">
{{ row.asyncPayMode ? '是' : '否' }}
<a-tag>{{ row.asyncPay ? '是' : '否' }}</a-tag>
</template>
</vxe-column>
<vxe-column field="combinationPayMode" title="组合支付">
<template #default="{ row }">
{{ row.combinationPayMode ? '是' : '否' }}
<a-tag>{{ row.combinationPayMode ? '是' : '否' }}</a-tag>
</template>
</vxe-column>
<vxe-column field="asyncPayChannel" title="异步支付方式">
<template #default="{ row }">
{{ dictConvert('PayChannel', row.asyncPayChannel) }}
<a-tag>{{ dictConvert('PayChannel', row.asyncChannel) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="expiredTime" title="过期时间" sortable />
<vxe-column field="createTime" title="创建时间" sortable />
<vxe-column fixed="right" width="120" :showOverflow="false" title="操作">
<template #default="{ row }">
@@ -62,14 +52,12 @@
<template #overlay>
<a-menu>
<a-menu-item>
<a-link @click="sync(row)">刷新信息</a-link>
<a-link @click="syncInfo(row)">同步</a-link>
</a-menu-item>
<a-menu-item v-if="[PayStatus.TRADE_PROGRESS].includes(row.payStatus)">
<a-link @click="cancel(row)" danger>关闭</a-link>
<a-menu-item v-if="[PayStatus.PROGRESS].includes(row.status)">
<a-link @click="closeOrder(row)" danger>关闭</a-link>
</a-menu-item>
<a-menu-item
v-if="[PayStatus.TRADE_SUCCESS, PayStatus.TRADE_REFUNDING].includes(row.payStatus) && row.refundableBalance > 0"
>
<a-menu-item v-if="[PayStatus.SUCCESS, PayStatus.PARTIAL_REFUND].includes(row.status) && row.refundableBalance > 0">
<a-link @click="refund(row)" danger>退款</a-link>
</a-menu-item>
</a-menu>
@@ -86,28 +74,27 @@
:total="pagination.total"
@page-change="handleTableChange"
/>
<payment-info ref="paymentInfo" />
<pay-order-info ref="payOrderInfo" />
<refund-model ref="refundModel" @ok="queryPage" />
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue'
import { computed, onMounted } from 'vue'
import { $ref } from 'vue/macros'
import { page } from './PayOrder.api'
import { close, page, sync } from './PayOrder.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import PaymentInfo from './PaymentInfo.vue'
import PayOrderInfo from './PayOrderInfo.vue'
import RefundModel from './RefundModel.vue'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { useMessage } from '/@/hooks/web/useMessage'
import { BOOLEAN, DATE_TIME, LIST, NUMBER, QueryField, QueryParam, STRING } from '/@/components/Bootx/Query/Query'
import { LIST, QueryField, STRING } from '/@/components/Bootx/Query/Query'
import { useDict } from '/@/hooks/bootx/useDict'
import { cancelByPaymentId, syncByBusinessId } from '/@/api/common/Pay'
import BSuperQuery from '/@/components/Bootx/SuperQuery/BSuperQuery.vue'
import { VxeTableInstance, VxeToolbarInstance, VxePager, VxeTable, VxeToolbar } from 'vxe-table'
import ALink from '/@/components/Link/Link.vue'
import { PayStatus } from '/@/enums/payment/PayStatus'
import { LabeledValue } from 'ant-design-vue/lib/select'
// 使用hooks
const { handleTableChange, pageQueryResHandel, resetQuery, resetQueryParams, pagination, pages, model, loading, superQueryFlag } =
@@ -115,15 +102,20 @@
const { notification, createMessage, createConfirm } = useMessage()
const { dictConvert, dictDropDown } = useDict()
let cayChannelList = $ref<LabeledValue[]>([])
let payStatusList = $ref<LabeledValue[]>([])
// 查询条件
const fields = [
{ field: 'paymentId', type: STRING, name: '支付ID', placeholder: '请输入支付ID' },
{ field: 'businessNo', type: STRING, name: '业务号', placeholder: '请输入业务号' },
{ field: 'title', type: STRING, name: '标题', placeholder: '请输入标题' },
{ field: 'errorCode', name: '错误码', type: STRING },
{ field: 'asyncPayChannel', name: '异步支付方式', type: LIST, selectList: dictDropDown('PayChannel') },
{ field: 'payStatus', name: '支付状态', type: LIST, selectList: dictDropDown('PayStatus') },
] as QueryField[]
const fields = computed(() => {
return [
{ field: 'paymentId', type: STRING, name: '支付ID', placeholder: '请输入支付ID' },
{ field: 'businessNo', type: STRING, name: '业务号', placeholder: '请输入业务号' },
{ field: 'title', type: STRING, name: '标题', placeholder: '请输入标题' },
{ field: 'errorCode', name: '错误码', type: STRING },
{ field: 'asyncPayChannel', name: '异步支付方式', type: LIST, selectList: cayChannelList },
{ field: 'payStatus', name: '支付状态', type: LIST, selectList: payStatusList },
] as QueryField[]
})
let sortParam = $ref({
sortField: 'createTime',
@@ -132,10 +124,11 @@
const xTable = $ref<VxeTableInstance>()
const xToolbar = $ref<VxeToolbarInstance>()
const paymentInfo = $ref<any>()
const payOrderInfo = $ref<any>()
const refundModel = $ref<any>()
onMounted(() => {
initData()
vxeBind()
queryPage()
})
@@ -143,6 +136,13 @@
xTable?.connect(xToolbar as VxeToolbarInstance)
}
/**
* 初始化数据
*/
async function initData() {
cayChannelList = await dictDropDown('AsyncPayChannel')
payStatusList = await dictDropDown('PayCallbackStatus')
}
/**
* 分页查询
*/
@@ -163,35 +163,44 @@
function sortChange({ order, property }) {
sortParam.sortField = order ? property : null
sortParam.asc = order === 'asc'
queryPage()
}
/**
* 查看
*/
function show(record) {
paymentInfo.init(record.id)
payOrderInfo.init(record.id)
}
/**
* 同步信息
*/
function sync(record) {
loading.value = true
syncByBusinessId(record.businessId).then(() => {
createMessage.success('信息同步成功')
queryPage()
function syncInfo(record) {
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否同步支付信息',
onOk: () => {
loading.value = true
sync(record.id).then(() => {
createMessage.success('同步成功')
queryPage()
})
},
})
}
/**
* 关闭支付
*/
function cancel(record) {
function closeOrder(record) {
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否关闭支付',
content: '是否关闭支付订单',
onOk: () => {
cancelByPaymentId(record.id).then(() => {
close(record.id).then(() => {
createMessage.success('关闭成功')
queryPage()
})
},

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="less">
</style>

View File

@@ -59,7 +59,7 @@ export interface RefundRecord extends BaseEntity {
*/
export interface RefundableInfo {
// 支付通道
payChannel?: number
channel?: number
// 金额
amount?: number
}

View File

@@ -59,7 +59,6 @@
const { notification, createMessage, createConfirm } = useMessage()
const { dictConvert, dictDropDown } = useDict()
//
let asyncPayChannelList = $ref<LabeledValue[]>([])
let PayCallbackStatusList = $ref<LabeledValue[]>([])
@@ -89,7 +88,7 @@
const payCallbackRecordInfo = $ref<any>()
onMounted(() => {
init()
initData()
vxeBind()
queryPage()
})
@@ -100,7 +99,7 @@
/**
* 初始化
*/
async function init() {
async function initData() {
asyncPayChannelList = await dictDropDown('AsyncPayChannel')
PayCallbackStatusList = await dictDropDown('PayCallbackStatus')
}