Browse Source

feat: pay refund

main
xingyu 2 years ago
parent
commit
e867d9a81b
  1. 3
      src/api/pay/channel/index.ts
  2. 9
      src/enums/systemEnum.ts
  3. 9
      src/utils/dict.ts
  4. 10
      src/views/pay/app/ChannelModal.vue
  5. 107
      src/views/pay/app/app.data.ts
  6. 19
      src/views/pay/app/index.vue
  7. 2
      src/views/pay/order/order.data.ts
  8. 237
      src/views/pay/refund/refund.data.ts

3
src/api/pay/channel/index.ts

@ -40,9 +40,8 @@ export function getChannelPage(params: ChannelPageReqVO) {
}
// 查询详情支付渠道
export function getChannel(merchantId: number, appId: string, code: string) {
export function getChannel(appId: string, code: string) {
const params = {
merchantId,
appId,
code,
}

9
src/enums/systemEnum.ts

@ -97,6 +97,10 @@ export const PayChannelEnum = {
code: 'wx_app',
name: '微信 APP 支付',
},
WX_BAR: {
code: 'wx_bar',
name: '微信条码支付',
},
ALIPAY_PC: {
code: 'alipay_pc',
name: '支付宝 PC 网站支付',
@ -117,6 +121,10 @@ export const PayChannelEnum = {
code: 'alipay_bar',
name: '支付宝条码支付',
},
MOCK: {
code: 'mock',
name: '模拟支付',
},
}
/**
@ -143,6 +151,7 @@ export const PayDisplayModeEnum = {
export const PayType = {
WECHAT: 'WECHAT',
ALIPAY: 'ALIPAY',
MOCK: 'MOCK',
}
/**

9
src/utils/dict.ts

@ -117,15 +117,6 @@ export enum DICT_TYPE {
BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
// ========== PAY 模块 ==========
PAY_CHANNEL_WECHAT_VERSION = 'pay_channel_wechat_version', // 微信渠道版本
PAY_CHANNEL_ALIPAY_SIGN_TYPE = 'pay_channel_alipay_sign_type', // 支付渠道支付宝算法类型
PAY_CHANNEL_ALIPAY_MODE = 'pay_channel_alipay_mode', // 支付宝公钥类型
PAY_CHANNEL_ALIPAY_SERVER_TYPE = 'pay_channel_alipay_server_type', // 支付宝网关地址
PAY_CHANNEL_CODE_TYPE = 'pay_channel_code_type', // 支付渠道编码类型
PAY_ORDER_NOTIFY_STATUS = 'pay_order_notify_status', // 商户支付订单回调状态
PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态
PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态
PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type', // 退款订单类别
PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态

10
src/views/pay/app/ChannelModal.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { aliPayFormSchema, weChatFormSchema } from './app.data'
import { aliPayFormSchema, mockFormSchema, weChatFormSchema } from './app.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
@ -19,7 +19,7 @@ const type = ref(PayType.ALIPAY)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: type.value === PayType.ALIPAY ? aliPayFormSchema : weChatFormSchema,
schemas: type.value === PayType.ALIPAY ? aliPayFormSchema : (type.value === PayType.WECHAT ? weChatFormSchema : mockFormSchema),
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
@ -30,7 +30,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
isUpdate.value = !!data?.isUpdate
type.value = data.type
if (unref(isUpdate)) {
const res = await getChannel(data.record.payMerchant.id, data.record.id, data.payCode)
const res = await getChannel(data.record.id, data.payCode)
const config = JSON.parse(res.config)
const payConfig: any = {}
if (type.value === PayType.ALIPAY) {
@ -44,7 +44,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
payConfig.alipayPublicCertContent = config.alipayPublicCertContent
payConfig.rootCertContent = config.rootCertContent
}
else {
else if (type.value === PayType.WECHAT) {
payConfig.appId = config.appId
payConfig.apiVersion = config.apiVersion
payConfig.mchId = config.mchId
@ -53,6 +53,8 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
payConfig.privateCertContent = config.privateCertContent
payConfig.apiV3Key = config.apiV3Key
}
// else if (type.value === PayType.MOCK) {
// }
res.payConfig = payConfig
delete res.config
setFieldsValue({ ...res })

107
src/views/pay/app/app.data.ts

@ -1,7 +1,6 @@
import { Switch } from 'ant-design-vue'
import { h } from 'vue'
import { changeAppStatus } from '@/api/pay/app'
import { getMerchantListByName } from '@/api/pay/merchant'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { PayChannelEnum } from '@/enums/systemEnum'
@ -19,12 +18,6 @@ export const columns: BasicColumn[] = [
dataIndex: 'name',
width: 100,
},
{
title: '应用名',
dataIndex: 'appId',
width: 100,
ifShow: false,
},
{
title: '开启状态',
dataIndex: 'status',
@ -57,14 +50,6 @@ export const columns: BasicColumn[] = [
})
},
},
{
title: '商户名称',
dataIndex: 'payMerchant.name',
width: 100,
customRender: ({ record }) => {
return record.payMerchant.name || ''
},
},
{
title: '支付宝配置',
children: [
@ -115,6 +100,16 @@ export const columns: BasicColumn[] = [
},
],
},
{
title: '模拟支付配置',
children: [
{
title: PayChannelEnum.MOCK.name,
dataIndex: PayChannelEnum.MOCK.code,
width: 160,
},
],
},
{
title: '创建时间',
dataIndex: 'createTime',
@ -132,12 +127,6 @@ export const searchFormSchema: FormSchema[] = [
component: 'Input',
colProps: { span: 8 },
},
{
label: '商户名称',
field: 'merchantName',
component: 'Input',
colProps: { span: 8 },
},
{
label: '开启状态',
field: 'status',
@ -168,17 +157,6 @@ export const formSchema: FormSchema[] = [
required: true,
component: 'Input',
},
{
label: '所属商户',
field: 'shortName',
required: true,
component: 'ApiSelect',
componentProps: {
api: () => getMerchantListByName(''),
labelField: 'name',
valueField: 'id',
},
},
{
label: '开启状态',
field: 'status',
@ -189,7 +167,7 @@ export const formSchema: FormSchema[] = [
},
{
label: '支付结果的回调地址',
field: 'payNotifyUrl',
field: 'orderNotifyUrl',
required: true,
component: 'Input',
},
@ -241,7 +219,10 @@ export const aliPayFormSchema: FormSchema[] = [
required: true,
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_ALIPAY_SERVER_TYPE, 'string'),
options: [
{ value: 'https://openapi.alipay.com/gateway.do', label: '线上环境' },
{ value: 'https://openapi-sandbox.dl.alipaydev.com/gateway.do', label: '沙箱环境' },
],
},
},
{
@ -250,7 +231,9 @@ export const aliPayFormSchema: FormSchema[] = [
required: true,
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_ALIPAY_SIGN_TYPE, 'string'),
options: [
{ value: 'RSA2', label: 'RSA2', key: 'RSA2' },
],
},
},
{
@ -260,18 +243,21 @@ export const aliPayFormSchema: FormSchema[] = [
defaultValue: null,
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_ALIPAY_MODE),
options: [
{ value: 1, label: '公钥模式', key: '1' },
{ value: 2, label: '证书模式', key: '2' },
],
},
},
{
label: '商户私钥',
label: '应用私钥',
field: 'payConfig.privateKey',
required: true,
ifShow: ({ values }) => !!(values['payConfig.mode'] === 1),
component: 'Input',
component: 'InputTextArea',
},
{
label: '支付宝公钥字符串',
label: '支付宝公钥',
field: 'payConfig.alipayPublicKey',
required: true,
ifShow: ({ values }) => !!(values['payConfig.mode'] === 1),
@ -320,7 +306,7 @@ export const weChatFormSchema: FormSchema[] = [
component: 'InputNumber',
},
{
label: '公众号APPID',
label: '公众号 APPID',
field: 'payConfig.appId',
required: true,
component: 'Input',
@ -346,7 +332,10 @@ export const weChatFormSchema: FormSchema[] = [
required: true,
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_WECHAT_VERSION, 'string'),
options: [
{ value: 'v2', label: 'v2' },
{ value: 'v3', label: 'v3' },
],
},
},
{
@ -354,25 +343,34 @@ export const weChatFormSchema: FormSchema[] = [
field: 'payConfig.mchKey',
required: true,
ifShow: ({ values }) => !!(values['payConfig.apiVersion'] === 'v2'),
component: 'Input',
component: 'InputTextArea',
},
{
label: 'apiclient_cert.p12 证书',
field: 'payConfig.keyContent',
required: true,
ifShow: ({ values }) => !!(values['payConfig.apiVersion'] === 'v2'),
component: 'InputTextArea',
},
{
label: 'API V3密钥',
field: 'payConfig.apiV3Key',
required: true,
ifShow: ({ values }) => !!(values['payConfig.apiVersion'] === 'v3'),
component: 'Input',
component: 'InputTextArea',
},
{
label: 'apiclient_key.perm证书',
field: 'payConfig.privateKeyContent',
required: true,
ifShow: ({ values }) => !!(values['payConfig.apiVersion'] === 'v3'),
component: 'InputTextArea',
},
{
label: 'apiclient_cert.perm证书',
field: 'payConfig.privateCertContent',
required: true,
ifShow: ({ values }) => !!(values['payConfig.apiVersion'] === 'v3'),
component: 'InputTextArea',
},
{
@ -381,3 +379,26 @@ export const weChatFormSchema: FormSchema[] = [
component: 'InputTextArea',
},
]
export const mockFormSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '渠道状态',
field: 'status',
required: true,
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]

19
src/views/pay/app/index.vue

@ -229,6 +229,25 @@ function openChannel(record: Recordable, payCode: string, type: string, isUpdate
<Icon icon="ant-design:close-outlined" />
</a-button>
</template>
<template v-if="column.key === PayChannelEnum.MOCK.code">
<a-button
v-if="record.channelCodes.indexOf(PayChannelEnum.MOCK.code) !== -1"
type="primary"
shape="circle"
@click="openChannel(record, PayChannelEnum.MOCK.code, PayType.MOCK, true)"
>
<Icon icon="ant-design:check-outlined" />
</a-button>
<a-button
v-else
type="primary"
shape="circle"
danger
@click="openChannel(record, PayChannelEnum.MOCK.code, PayType.MOCK, false)"
>
<Icon icon="ant-design:close-outlined" />
</a-button>
</template>
<template v-if="column.key === 'action'">
<TableAction
:actions="[

2
src/views/pay/order/order.data.ts

@ -115,7 +115,7 @@ export const searchFormSchema: FormSchema[] = [
field: 'channelCode',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE),
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE),
},
colProps: { span: 8 },
},

237
src/views/pay/refund/refund.data.ts

@ -1,4 +1,4 @@
import { getMerchantListByName } from '@/api/pay/merchant'
import { getAppList } from '@/api/pay/app'
import type { DescItem } from '@/components/Description'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
@ -11,100 +11,72 @@ export const columns: BasicColumn[] = [
width: 100,
},
{
title: '支付渠道',
title: '支付金额(元)',
dataIndex: 'payPrice',
width: 120,
customRender: ({ text }) => {
return `${Number.parseFloat(`${text / 100}`).toFixed(2)}`
},
},
{
title: '退款金额(元)',
dataIndex: 'refundPrice',
width: 120,
customRender: ({ text }) => {
return `${Number.parseFloat(`${text / 100}`).toFixed(2)}`
},
},
{
title: '退款订单号',
children: [
{
title: '商户名称',
dataIndex: 'merchantName',
title: '商户',
dataIndex: 'merchantRefundId',
width: 120,
},
{
title: '应用名称',
dataIndex: 'appName',
title: '退款',
dataIndex: 'no',
width: 120,
},
{
title: '渠道名称',
dataIndex: 'channelCodeName',
title: '渠道',
dataIndex: 'channelRefundNo',
width: 160,
},
],
},
{
title: '商户订单号',
children: [
{
title: '退款',
dataIndex: 'merchantRefundNo',
width: 200,
},
{
title: '交易',
dataIndex: 'merchantOrderId',
width: 100,
},
],
},
{
title: '支付订单号',
children: [
{
title: '交易',
dataIndex: 'tradeNo',
width: 100,
title: '商户',
dataIndex: 'merchantOrderId',
width: 200,
},
{
title: '渠道',
dataIndex: 'channelOrderNo',
width: 200,
width: 100,
},
],
},
{
title: '支付金额(元)',
dataIndex: 'payAmount',
width: 120,
customRender: ({ text }) => {
return `${Number.parseFloat(text / 100).toFixed(2)}`
},
},
{
title: '退款金额(元)',
dataIndex: 'refundAmount',
width: 120,
customRender: ({ text }) => {
return `${Number.parseFloat(text / 100).toFixed(2)}`
},
},
{
title: '退款类型',
dataIndex: 'type',
width: 100,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.PAY_REFUND_ORDER_TYPE)
},
},
{
title: '退款状态',
dataIndex: 'status',
width: 100,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.PAY_REFUND_ORDER_STATUS)
return useRender.renderDict(text, DICT_TYPE.PAY_REFUND_STATUS)
},
},
{
title: '回调状态',
dataIndex: 'notifyStatus',
title: '退款渠道',
dataIndex: 'channelCode',
width: 100,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.PAY_ORDER_NOTIFY_STATUS)
return useRender.renderDict(text, DICT_TYPE.PAY_CHANNEL_CODE)
},
},
{
title: '退款原因',
dataIndex: 'reason',
width: 100,
},
{
title: '创建时间',
dataIndex: 'createTime',
@ -121,51 +93,40 @@ export const columns: BasicColumn[] = [
return useRender.renderDate(text)
},
},
{
title: '支付应用',
dataIndex: 'appName',
width: 100,
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '所属商户',
field: 'merchantId',
component: 'ApiSelect',
componentProps: {
api: () => getMerchantListByName(''),
},
colProps: { span: 8 },
},
{
label: '应用编号',
field: 'appId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '渠道编码',
field: 'channelCode',
component: 'Select',
component: 'ApiSelect',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE),
api: () => getAppList(),
labelField: 'name',
valueField: 'id',
},
colProps: { span: 8 },
},
{
label: '退款类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_REFUND_ORDER_TYPE),
},
label: '商户支付单号',
field: 'merchantOrderId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '商户退款单号',
field: 'merchantRefundNo',
label: '商户退款单号',
field: 'merchantRefundId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '应用编号',
field: 'appId',
label: '渠道支付单号',
field: 'channelOrderNo',
component: 'Input',
colProps: { span: 8 },
},
@ -174,16 +135,7 @@ export const searchFormSchema: FormSchema[] = [
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_REFUND_ORDER_STATUS),
},
colProps: { span: 8 },
},
{
label: '退款回调状态',
field: 'notifyStatus',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
options: getDictOptions(DICT_TYPE.PAY_REFUND_STATUS),
},
colProps: { span: 8 },
},
@ -197,54 +149,51 @@ export const searchFormSchema: FormSchema[] = [
export const descSchema: DescItem[] = [
{
label: '商户名称',
field: 'merchantName',
label: '商户退款单号',
field: 'merchantRefundId',
},
{
label: '应用名称',
field: 'appName',
label: '渠道退款单号',
field: 'channelRefundNo',
},
{
label: '商品名称',
field: 'subject',
label: '商户支付单号',
field: 'merchantOrderId',
},
{
label: '商户退款单号',
field: 'merchantRefundNo',
label: '渠道支付单号',
field: 'channelOrderNo',
render: (curVal) => {
return useRender.renderTag(curVal)
},
},
{
label: '商户订单号',
field: 'merchantOrderId',
label: '应用编号',
field: 'appId',
},
{
label: '支付金额',
field: 'payAmount',
render: (curVal) => {
return `${Number.parseFloat(curVal / 100).toFixed(2)}`
},
label: '应用名称',
field: 'appName',
},
{
label: '退款金额',
field: 'refundAmount',
label: '支付金额',
field: 'payPrice',
render: (curVal) => {
return `${Number.parseFloat(curVal / 100).toFixed(2)}`
return `${Number.parseFloat(`${curVal / 100}`).toFixed(2)}`
},
},
{
label: '退款类型',
field: 'type',
label: '退款金额',
field: 'refundPrice',
render: (curVal) => {
return useRender.renderDict(curVal, DICT_TYPE.PAY_REFUND_ORDER_TYPE)
return `${Number.parseFloat(`${curVal / 100}`).toFixed(2)}`
},
},
{
label: '退款状态',
field: 'status',
render: (curVal) => {
return useRender.renderDict(curVal, DICT_TYPE.PAY_REFUND_ORDER_STATUS)
return useRender.renderDict(curVal, DICT_TYPE.PAY_REFUND_STATUS)
},
},
{
@ -261,13 +210,6 @@ export const descSchema: DescItem[] = [
return useRender.renderDate(curVal)
},
},
{
label: '退款失效时间',
field: 'expireTime',
render: (curVal) => {
return useRender.renderDate(curVal)
},
},
{
label: '更新时间',
field: 'updateTime',
@ -276,38 +218,23 @@ export const descSchema: DescItem[] = [
},
},
{
label: '支付渠道',
field: 'channelCodeName',
},
{
label: '支付IP',
field: 'userIp',
},
{
label: '回调地址',
field: 'notifyUrl',
},
{
label: '回调状态',
field: 'notifyStatus',
label: '退款渠道',
field: 'channelCode',
render: (curVal) => {
return useRender.renderDict(curVal, DICT_TYPE.PAY_ORDER_NOTIFY_STATUS)
return useRender.renderDict(curVal, DICT_TYPE.PAY_CHANNEL_CODE)
},
},
{
label: '回调时间',
field: 'notifyTime',
render: (curVal) => {
return useRender.renderDate(curVal)
},
label: '退款原因',
field: 'reason',
},
{
label: '渠道订单号',
field: 'channelOrderNo',
label: '退款 IP',
field: 'userIp',
},
{
label: '渠道退款单号',
field: 'channelRefundNo',
label: '退款 URL',
field: 'notifyUrl',
},
{
label: '渠道错误码',
@ -315,14 +242,10 @@ export const descSchema: DescItem[] = [
},
{
label: '渠道错误码描述',
field: 'notifchannelErrorMsgyUrl',
field: 'channelErrorMsg',
},
{
label: '渠道额外参数',
field: 'channelExtras',
},
{
label: '退款原因',
field: 'reason',
label: '支付通道异步回调内容',
field: 'channelNotifyData',
},
]