feat 云闪付配置

This commit is contained in:
DaxPay
2024-09-12 17:36:24 +08:00
parent c55fab07e0
commit 736373c5dc
5 changed files with 363 additions and 10 deletions

View File

@@ -0,0 +1,51 @@
import { defHttp } from '@/utils/http/axios'
import { Result } from '#/axios'
import { MchEntity } from '#/web'
import { unref } from 'vue'
/**
* 获取单条
*/
export function getConfig(id) {
return defHttp.get<Result<UnionPayConfig>>({
url: '/union/pay/config/findById',
params: { id: unref(id) },
})
}
/**
* 保存或更新
*/
export function saveOrUpdate(obj: UnionPayConfig) {
return defHttp.post({
url: '/union/pay/config/saveOrUpdate',
data: obj,
})
}
/**
* 微信支付配置
*/
export interface UnionPayConfig extends MchEntity {
// 商户号
unionMachId?: string
// 商户收款账号
seller?: string
// 是否启用
enable: boolean
// 支付限额
limitAmount?: number
// 签名类型
signType?: string
// 是否为证书签名
certSign?: boolean
// 应用私钥证书
keyPrivateCert?: string
// 私钥证书对应的密码
keyPrivateCertPwd?: string
// 中级证书
acpMiddleCert?: string
// 根证书
acpRootCert?: string
// 是否沙箱环境
sandbox?: boolean
}

View File

@@ -0,0 +1,300 @@
<template>
<basic-drawer
showFooter
v-bind="$attrs"
width="60%"
title="云闪付支付配置"
:open="visible"
:maskClosable="false"
@close="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" name="id" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="商户号" name="unionMachId">
<a-input
v-model:value="form.unionMachId"
:disabled="showable"
placeholder="请输入商户号"
/>
</a-form-item>
<a-form-item label="是否启用" name="enable">
<a-switch
checked-children="启用"
un-checked-children="停用"
v-model:checked="form.enable"
/>
</a-form-item>
<a-form-item name="limitAmount">
<template #label>
<basic-title
helpMessage="每次发起支付的金额不能超过该值,如果同时配置了全局支付限额,则以额度低的为准"
>
支付限额()
</basic-title>
</template>
<a-input-number
:precision="2"
:min="0.01"
v-model:value="form.limitAmount"
placeholder="请输入支付限额(元)"
/>
</a-form-item>
<a-form-item label="签名类型" name="signType">
<a-select
allowClear
v-model:value="form.signType"
style="width: 100%"
placeholder="选择签名类型"
>
<a-select-option key="RSA2">RSA2秘钥</a-select-option>
</a-select>
</a-form-item>
<a-form-item name="keyPrivateCert" label="应用私钥证书">
<a-upload
v-if="!form.keyPrivateCert"
:disabled="showable"
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
@change="(info) => handleChange(info, 'keyPrivateCert')"
>
<a-button type="primary" preIcon="carbon:cloud-upload"> 证书上传 </a-button>
</a-upload>
<a-input v-else defaultValue="acp_enc.cer" disabled>
<template #addonAfter v-if="!showable">
<a-tooltip>
<template #title> 删除上传的证书文件 </template>
<icon
@click="form.keyPrivateCert = ''"
icon="ant-design:close-circle-outlined"
:size="20"
/>
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item label="私钥证书密码" name="keyPrivateCertPwd">
<a-input
allow-clear
v-model:value="form.keyPrivateCertPwd"
:disabled="showable"
placeholder="请输入私钥证书密码"
/>
</a-form-item>
<a-form-item name="acpMiddleCert" label="中级证书">
<a-upload
v-if="!form.acpMiddleCert"
:disabled="showable"
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
@change="(info) => handleChange(info, 'acpMiddleCert')"
>
<a-button type="primary" preIcon="carbon:cloud-upload"> 证书上传 </a-button>
</a-upload>
<a-input v-else defaultValue="acp_middle.cer" disabled>
<template #addonAfter v-if="!showable">
<a-tooltip>
<template #title> 删除上传的证书文件 </template>
<icon
@click="form.acpMiddleCert = ''"
icon="ant-design:close-circle-outlined"
:size="20"
/>
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item name="acpRootCert" label="根证书">
<a-upload
v-if="!form.acpRootCert"
:disabled="showable"
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
@change="(info) => handleChange(info, 'acpRootCert')"
>
<a-button type="primary" preIcon="carbon:cloud-upload"> 证书上传 </a-button>
</a-upload>
<a-input v-else defaultValue="acp_root.cer" disabled>
<template #addonAfter v-if="!showable">
<a-tooltip>
<template #title> 删除上传的证书文件 </template>
<icon
@click="form.acpRootCert = ''"
icon="ant-design:close-circle-outlined"
:size="20"
/>
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item label="测试环境" name="sandbox">
<a-switch checked-children="" un-checked-children="" v-model:checked="form.sandbox" />
</a-form-item>
</a-form>
</a-spin>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button
v-if="!showable"
key="forward"
:loading="confirmLoading"
type="primary"
@click="handleOk"
>保存</a-button
>
</a-space>
</template>
</basic-drawer>
</template>
<script lang="ts" setup>
import { computed, nextTick, ref } from 'vue'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { BasicDrawer } from '@/components/Drawer'
import Icon from '@/components/Icon/Icon.vue'
import { useUpload } from '@/hooks/bootx/useUpload'
import { useMessage } from '@/hooks/web/useMessage'
import BasicTitle from '@/components/Basic/src/BasicTitle.vue'
import { getConfig, saveOrUpdate, UnionPayConfig } from './UnionPayConfig.api'
import { ChannelConfig } from '@/views/daxpay/common/merchant/channel/ChannelConfig.api'
const { handleCancel, diffForm, labelCol, wrapperCol, confirmLoading, visible, showable } =
useFormEdit()
// 文件上传
const { tokenHeader, uploadAction } = useUpload('/readBase64')
const { createMessage } = useMessage()
const channelConfig = ref<ChannelConfig>({})
// 表单
const formRef = ref<FormInstance>()
let rawForm: any = {}
let form = ref<UnionPayConfig>({
id: null,
limitAmount: 20000,
signType: 'RSA2',
enable: false,
sandbox: false,
})
// 校验
const rules = computed(() => {
return {
unionMachId: [{ required: true, message: '请输入商户号' }],
wxAppId: [{ required: true, message: '请输入应用编号' }],
limitAmount: [{ required: true, message: '请输入单次支付限额' }],
// certSign: [{ required: true, message: '请选择是否为证书签名' }],
signType: [{ required: true, message: '请选择签名类型' }],
keyPrivateCert: [{ required: true, message: '请上传应用私钥证书' }],
keyPrivateCertPwd: [{ required: true, message: '请输入应用私钥证书密码' }],
acpMiddleCert: [{ required: true, message: '请上传中级证书' }],
acpRootCert: [{ required: true, message: '请上传根证书' }],
enable: [{ required: true, message: '请选择是否启用' }],
notifyUrl: [{ required: true, message: '请输入异步通知页面地址' }],
returnUrl: [{ required: true, message: '请输入同步通知页面地址' }],
apiVersion: [{ required: true, message: '请选择支付API版本' }],
apiKeyV2: [{ required: true, message: '请输入V2秘钥' }],
sandbox: [{ required: true, message: '请选择是否为沙箱环境' }],
payWays: [{ required: true, message: '请选择支持的支付类型' }],
} as Record<string, Rule[]>
})
// 事件
const emits = defineEmits(['ok'])
/**
* 入口
*/
function init(config: ChannelConfig) {
visible.value = true
confirmLoading.value = false
channelConfig.value = config
resetForm()
getInfo()
}
// 获取信息
async function getInfo() {
if (channelConfig.value.id) {
confirmLoading.value = true
getConfig(channelConfig.value.id).then(({ data }) => {
rawForm = { ...data }
form.value = data
confirmLoading.value = false
})
}
}
// 保存
function handleOk() {
formRef.value?.validate().then(async () => {
confirmLoading.value = true
await saveOrUpdate({
...form.value,
...diffForm(
rawForm,
form.value,
'keyPrivateCert',
'keyPrivateCertPwd',
'acpMiddleCert',
'acpRootCert',
),
mchNo: channelConfig.value.mchNo,
appId: channelConfig.value.appId,
}).finally(() => {
confirmLoading.value = false
})
handleCancel()
createMessage.success('保存成功')
emits('ok')
})
}
/**
* 重置表单
*/
function resetForm() {
nextTick(() => {
formRef.value?.resetFields()
})
}
/**
* 文件上传
*/
function handleChange(info, fieldName) {
// 上传完毕
if (info.file.status === 'done') {
const res = info.file.response
if (!res.code) {
form.value[fieldName] = res.data
createMessage.success(`${info.file.name} 上传成功!`)
} else {
createMessage.error(`${res.msg}`)
}
} else if (info.file.status === 'error') {
createMessage.error('上传失败')
}
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -205,7 +205,7 @@
import { getConfig, saveOrUpdate, WechatPayConfig } from './WechatPayConfig.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { BasicDrawer } from '@/components/Drawer'
import Icon from '../../../../../../components/Icon/Icon.vue'
import Icon from '@/components/Icon/Icon.vue'
import { useUpload } from '@/hooks/bootx/useUpload'
import { useMessage } from '@/hooks/web/useMessage'
import BasicTitle from '@/components/Basic/src/BasicTitle.vue'

View File

@@ -28,7 +28,11 @@
:loading="loading"
>
<vxe-column type="seq" width="60" />
<vxe-column field="appId" title="应用号" :min-width="100" />
<vxe-column field="appId" title="应用号" :min-width="100" >
<template #default="{ row }">
<a-link @click="show(row)">{{ row.appId }}</a-link>
</template>
</vxe-column>
<vxe-column field="appName" title="应用名称" :min-width="100" />
<vxe-column field="signType" title="签名方式" :min-width="150">
<template #default="{ row }">
@@ -42,19 +46,16 @@
</vxe-column>
<vxe-column field="notifyUrl" title="通知地址" :min-width="220" />
<vxe-column field="createTime" title="创建时间" :min-width="120" />
<vxe-column fixed="right" :width="180" :showOverflow="false" title="操作">
<vxe-column fixed="right" :width="200" :showOverflow="false" title="操作">
<template #default="{ row }">
<a-link @click="show(row)">查看</a-link>
<a-divider type="vertical" />
<a-link @click="edit(row)">编辑</a-link>
<a-divider type="vertical" />
<a-link @click="showChannelSetup(row)">通道配置</a-link>
<a-divider type="vertical" />
<a-dropdown>
<a> 更多 <Icon icon="ant-design:down-outlined" :size="12" /> </a>
<template #overlay>
<a-menu>
<a-menu-item>
<a-link @click="showChannelSetup(row)">通道配置</a-link>
</a-menu-item>
<a-menu-item>
<a-link @click="showNotifyConfig(row)">订阅配置</a-link>
</a-menu-item>
@@ -64,7 +65,6 @@
</a-menu>
</template>
</a-dropdown>
<a-divider type="vertical" />
</template>
</vxe-column>
</vxe-table>

View File

@@ -1,14 +1,16 @@
<template>
<alipay-config-edit ref="alipay" @ok="ok" />
<wechat-pay-config-edit ref="wechat" @ok="ok" />
<union-pay-config-edit ref="union" @ok="ok" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useMessage } from '@/hooks/web/useMessage'
import { PaymentEnum } from '@/enums/daxpay/PaymentEnum'
import { ChannelConfig } from './ChannelConfig.api'
import AlipayConfigEdit from '@/views/daxpay/common/channel/alipay/config/AlipayConfigEdit.vue'
import WechatPayConfigEdit from '@/views/daxpay/common/channel/wechat/config/WechatPayConfigEdit.vue'
import { ref } from 'vue'
import UnionPayConfigEdit from '@/views/daxpay/common/channel/union/config/UnionPayConfigEdit.vue'
const { createMessage } = useMessage()