feat(daxpay): 新增收银台支付功能

- 新增收银台支付相关的枚举和接口定义
- 实现收银台支付页面组件和相关API- 更新路由配置,添加收银台支付页面路由
- 修改现有组件,使用新的枚举定义
This commit is contained in:
bootx
2024-12-04 22:42:52 +08:00
parent 7412e0b565
commit aea858a6fc
14 changed files with 465 additions and 25 deletions

View File

@@ -1,12 +1,24 @@
/**
*
*/
export enum DaxpayEnum {
export enum ChannelEnum {
ALI = 'ali_pay',
WECHAT = 'wechat_pay',
UNION_PAY = 'union_pay',
}
/**
*
*/
export enum PayMethodEnum {
WAP = 'wap',
APP = 'app',
WEB = 'web',
QRCODE = 'qrcode',
BARCODE = 'barcode',
JSAPI = 'jsapi',
}
/**
*
*/
@@ -51,10 +63,36 @@ export enum ChannelAuthStatusEnum {
NOT_EXIST = 'not_exist',
}
/**
*
*/
export enum CashierTypeEnum {
WECHAT_PAY = 'wechat_pay',
ALIPAY = 'alipay',
export enum CheckoutTypeEnum {
H5 = 'h5',
PC = 'pc',
MINI_APP = 'mini_app',
AGGREGATE = 'aggregate',
}
/**
*
*/
export enum CheckoutCallTypeEnum {
/** 扫码支付 */
QR_CODE = 'qr_code',
/** 条码支付 */
BAR_CODE = 'bar_code',
/** 跳转链接 */
LINK = 'link',
/** 小程序 */
MINI_APP = 'mini_app',
/** APP支付 */
APP = 'app',
/** JSAPI */
JSAPI = 'jsapi',
/** 表单方式 */
FROM = 'from',
}

View File

@@ -36,12 +36,12 @@ export const OUTSIDE: AppRouteModule = {
name: 'PROJECT_OUTSIDE',
meta: { title: '' },
children: [
// {
// path: '/cashier',
// name: 'cashier',
// component: () => import('@/views/demo/cashier/Cashier.vue'),
// meta: { title: '收银台演示', ignoreAuth: true },
// },
{
path: '/checkout/:orderNo',
name: 'CheckoutPay',
component: () => import('@/views/daxpay/outside/checkout/CheckoutPay.vue'),
meta: { title: '收银台', ignoreAuth: true },
},
],
}

View File

@@ -83,7 +83,7 @@
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { mchAppDropdown } from '@/views/daxpay/common/merchant/app/MchApp.api'
import { useDict } from '@/hooks/bootx/useDict'
import { ChannelAuthStatusEnum } from '@/enums/daxpay/daxpayEnum'
import { ChannelAuthStatusEnum } from '@/enums/daxpay/channelEnum'
const { createMessage } = useMessage()
const { dictDropDown } = useDict()

View File

@@ -19,7 +19,7 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { TradeTypeEnum } from '@/enums/daxpay/daxpayEnum'
import { TradeTypeEnum } from '@/enums/daxpay/channelEnum'
import { useDict } from '@/hooks/bootx/useDict'
import { LabeledValue } from 'ant-design-vue/lib/select'
import DevelopTradePay from './DevelopTradePay.vue'

View File

@@ -6,7 +6,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useMessage } from '@/hooks/web/useMessage'
import { DaxpayEnum } from '@/enums/daxpay/daxpayEnum'
import { ChannelEnum } from '@/enums/daxpay/channelEnum'
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'
@@ -24,15 +24,15 @@
*/
function show(record: ChannelConfig) {
switch (record.channel) {
case DaxpayEnum.ALI: {
case ChannelEnum.ALI: {
alipay.value.init(record)
break
}
case DaxpayEnum.WECHAT: {
case ChannelEnum.WECHAT: {
wechat.value.init(record)
break
}
case DaxpayEnum.UNION_PAY: {
case ChannelEnum.UNION_PAY: {
union.value.init(record)
break
}

View File

@@ -54,7 +54,7 @@
import { BasicDrawer } from '@/components/Drawer'
import { ref } from 'vue'
import { findAll, ChannelConfig } from './ChannelConfig.api'
import { DaxpayEnum } from '@/enums/daxpay/daxpayEnum'
import { ChannelEnum } from '@/enums/daxpay/channelEnum'
import alipay from '@/assets/daxpay/alipay.svg'
import wechat from '@/assets/daxpay/wechat.svg'
import unionPay from '@/assets/daxpay/unionPay.svg'
@@ -112,11 +112,11 @@
*/
function getIcon(type: string) {
switch (type) {
case DaxpayEnum.ALI:
case ChannelEnum.ALI:
return alipay
case DaxpayEnum.WECHAT:
case ChannelEnum.WECHAT:
return wechat
case DaxpayEnum.UNION_PAY:
case ChannelEnum.UNION_PAY:
return unionPay
default:
return ''

View File

@@ -88,7 +88,7 @@
import PayOrderInfo from '@/views/daxpay/common/order/pay/PayOrderInfo.vue'
import TransferOrderInfo from '@/views/daxpay/common/order/transfer/TransferOrderInfo.vue'
import NotifyRecordList from './NotifyRecordList.vue'
import { NotifyContentTypeEnum } from '@/enums/daxpay/daxpayEnum'
import { NotifyContentTypeEnum } from '@/enums/daxpay/channelEnum'
import RefundOrderInfo from '@/views/daxpay/common/order/refund/RefundOrderInfo.vue'
import { mchAppDropdown } from '@/views/daxpay/common/merchant/app/MchApp.api'

View File

@@ -118,7 +118,7 @@
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { LabeledValue } from 'ant-design-vue/lib/select'
import { useDict } from '@/hooks/bootx/useDict'
import { TradeTypeEnum } from '@/enums/daxpay/daxpayEnum'
import { TradeTypeEnum } from '@/enums/daxpay/channelEnum'
import ReconcileDiscrepancyInfo from './ReconcileDiscrepancyInfo.vue'
import ReconcileStatementInfo from '../statement/ReconcileStatementInfo.vue'
import PayOrderInfo from '@/views/daxpay/common/order/pay/PayOrderInfo.vue'

View File

@@ -86,7 +86,7 @@
import PayOrderInfo from '@/views/daxpay/common/order/pay/PayOrderInfo.vue'
import RefundOrderInfo from '@/views/daxpay/common/order/refund/RefundOrderInfo.vue'
import TransferOrderInfo from '@/views/daxpay/common/order/transfer/TransferOrderInfo.vue'
import { TradeTypeEnum } from '@/enums/daxpay/daxpayEnum'
import { TradeTypeEnum } from '@/enums/daxpay/channelEnum'
import { mchAppDropdown } from '@/views/daxpay/common/merchant/app/MchApp.api'
// 使用hooks

View File

@@ -96,7 +96,7 @@
import TransferOrderInfo from '@/views/daxpay/common/order/transfer/TransferOrderInfo.vue'
import ALink from '@/components/Link/Link.vue'
import TradeFlowRecordInfo from './TradeFlowRecordInfo.vue'
import { TradeTypeEnum } from '@/enums/daxpay/daxpayEnum'
import { TradeTypeEnum } from '@/enums/daxpay/channelEnum'
import { mchAppDropdown } from '@/views/daxpay/common/merchant/app/MchApp.api'
// 使用hooks

View File

@@ -82,7 +82,7 @@
import BQuery from '@/components/Bootx/Query/BQuery.vue'
import { LIST, QueryField, STRING } from '@/components/Bootx/Query/Query'
import { useDict } from '@/hooks/bootx/useDict'
import { TradeTypeEnum } from '@/enums/daxpay/daxpayEnum'
import { TradeTypeEnum } from '@/enums/daxpay/channelEnum'
import { LabeledValue } from 'ant-design-vue/lib/select'
import ALink from '/@/components/Link/Link.vue'
import PayOrderInfo from '@/views/daxpay/common/order/pay/PayOrderInfo.vue'

View File

@@ -0,0 +1,146 @@
import { defHttp } from '@/utils/http/axios'
import { Result } from '#/axios'
import {PayResult} from "@/views/daxpay/common/develop/trade/DevelopTrade.api";
/**
* 获取收银台订单和配置信息
*/
export function getOrderAndConfig(orderNo, checkoutType) {
return defHttp.get({
url: '/unipay/checkout/getOrderAndConfig',
params: {
orderNo,
checkoutType,
},
})
}
/**
* 发起普通支付
*/
export function getCheckoutUrl(orderNo,checkoutType) {
return defHttp.get<Result<string>>({
url: '/unipay/checkout/getCheckoutUrl',
params: {orderNo,checkoutType},
})
}
/**
* 发起普通支付
*/
export function checkoutPay(param: CheckoutPayParam) {
return defHttp.post<Result<PayResult>>({
url: '/unipay/checkout/pay',
data: param,
})
}
/**
* 收银台支付参数
*/
export interface CheckoutPayParam {
/** 订单号 */
orderNo?: string
/** 支付配置项ID */
itemId?: string
/** 唯一标识 */
openId?: string
/** 付款码 */
barCode?: string
}
/**
* 收银台配置
*/
export interface CheckoutOrderAndConfigResult {
/** 订单信息 */
order: CheckoutOrderResult
/** 收银台配置信息 */
config: CheckoutConfigResult
/** 收银台分类配置信息 */
groupConfigs: CheckoutGroupConfigResult[]
}
/**
* 收银台聚合支付配置
*/
export interface AggregateOrderAndConfigResult {
/** 订单信息 */
order: CheckoutOrderResult
/** 收银台配置信息 */
config: CheckoutConfigResult
/** 收银台聚合配置信息 */
aggregateConfig: AggregateConfigResult
}
/**
* 订单信息
*/
export interface CheckoutOrderResult {
/** 商户订单号 */
bizOrderNo?: string
/** 订单号 */
orderNo?: string
/** 标题 */
title?: string
/** 描述 */
description?: string
/** 金额(元) */
amount?: string
}
/**
* 收银台配置信息
*/
export interface CheckoutConfigResult {
/** 收银台名称 */
name?: string
/** PC收银台是否同时显示聚合收银码 */
aggregateShow?: boolean
/** h5收银台自动升级聚合支付 */
h5AutoUpgrade?: boolean
}
/**
* 收银台分类配置
*/
export interface CheckoutGroupConfigResult {
/** 主键 */
id?: string
/** 类型 */
type?: string
/** 名称 */
name?: string
/** 配置项列表 */
items?: CheckoutItemConfigResult[]
}
/**
* 收银台配置项
*/
export interface CheckoutItemConfigResult {
/** 主键 */
id?: string
/** 发起调用的类型 */
callType?: string
/** 名称 */
name?: string
/** 支付通道 */
channel?: string
/** 支付方式 */
payMethod?: string
}
/**
* 收银台聚合配置信息
*/
export interface AggregateConfigResult {
/** 支付类型 */
type?: string
/** 通道 */
channel?: string
/** 支付方式 */
payMethod?: string
/** 自动拉起支付 */
autoLaunch?: boolean
}

View File

@@ -0,0 +1,177 @@
<template>
<div>
<div class="page paydemo">
<div class="blog-container" id="container">
<a-spin :spinning="loading">
<div class="content" style="padding-top: 70px">
<div style="width: 100%">
<div class="paydemo-type-content" style="display: flex;height: 300px">
<div style="width: 50%;">
<div class="paydemo-type-name">支付信息</div>
<a-form>
<div class="paydemo-form-item">
<label>标题</label>
<span>
{{ orderAndConfig.order.title }}
</span>
</div>
<div class="paydemo-form-item">
<label>商户订单号</label>
<span>
{{ orderAndConfig.order.bizOrderNo }}
</span>
</div>
<div class="paydemo-form-item">
<label>订单号</label>
<span>
{{ orderAndConfig.order.orderNo }}
</span>
</div>
<div class="paydemo-form-item">
<label>描述</label>
<span>
{{ orderAndConfig.order.description }}
</span>
</div>
</a-form>
</div>
<div style="width: 50%;display: flex;flex-direction:column;align-items: center" v-if="aggregateUrl">
<a-alert message="请使用支付宝、微信等应用扫码支付" type="info" style="width: 250px;margin-bottom: 10px"/>
<a-qrcode :value="aggregateUrl" :size="200" />
</div>
</div>
<div class="paydemo-type-content" style="height: 350px">
<a-tabs v-model:activeKey="activeKey" type="card">
<a-tab-pane v-for="group in orderAndConfig.groupConfigs" :key="group.id" :tab="group.name">
<div class="paydemo-type-body">
<div @click="()=>currentItem=config" v-for="config in group.items" :key="config.id">
<div :class="config.id === currentItem.id ? 'colorChange' : 'paydemoType'">
<span class="color-change">{{ config.name }}</span>
</div>
</div>
</div>
</a-tab-pane>
</a-tabs>
</div>
<div style="margin-top: 20px; text-align: right">
<span style="color: #fd482c; font-size: 18px; padding-right: 10px">{{ '¥ ' + orderAndConfig.order.amount }} </span>
<a-button type="primary" :disabled="!currentItem" @click="pay">立即支付</a-button>
</div>
</div>
</div>
</a-spin>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {useRoute} from "vue-router";
import {onMounted, ref} from "vue";
import {
CheckoutItemConfigResult,
CheckoutOrderAndConfigResult, checkoutPay, CheckoutPayParam, getCheckoutUrl,
getOrderAndConfig
} from "./CheckoutPay.api";
import {CheckoutCallTypeEnum, CheckoutTypeEnum,} from "@/enums/daxpay/channelEnum";
import {useMessage} from "@/hooks/web/useMessage";
const route = useRoute()
const { orderNo } = route.params
const activeKey = ref<string>()
const { createMessage } = useMessage()
let payForm = ref<string>()
const aggregateUrl = ref('1')
// 当前选择支付渠道和方式
let currentItem = ref<CheckoutItemConfigResult>({})
const loading = ref(false)
const orderAndConfig = ref<CheckoutOrderAndConfigResult>({
order: {},
config: {},
groupConfigs: [],
})
/**
* 初始化
*/
onMounted(() => {
initData()
})
/**
* 初始化数据
*/
async function initData() {
// 获取收银台配置
await getOrderAndConfig(orderNo, CheckoutTypeEnum.PC).then(({ data }) => {
orderAndConfig.value = data
activeKey.value = orderAndConfig.value.groupConfigs?.[0].id
currentItem.value = orderAndConfig.value.groupConfigs?.[0].items?.[0] as CheckoutItemConfigResult
})
// 是否同时显示聚合支付码
if (orderAndConfig.value.config.aggregateShow){
getCheckoutUrl(orderNo, CheckoutTypeEnum.AGGREGATE).then(({data})=>{
aggregateUrl.value = data
})
}
}
/**
* 发起支付
*/
function pay() {
// 条码支付
if (CheckoutCallTypeEnum.BAR_CODE === currentItem.value.callType){
}
// 支付调起类型
switch (currentItem.value.callType){
case CheckoutCallTypeEnum.LINK:{
break;
}
case CheckoutCallTypeEnum.FROM:{
break;
}
case CheckoutCallTypeEnum.QR_CODE:{
break;
}
case CheckoutCallTypeEnum.BAR_CODE:{
break;
}
default:{
createMessage.warning('暂不支持该支付方式')
}
}
loading.value = true
const from = {
orderNo,
itemId: currentItem.value.id,
} as CheckoutPayParam
checkoutPay(from).then(({ data }) => {
loading.value = false
// 跳转到支付页面
location.replace(data.payBody as string)
})
}
</script>
<style scoped lang="less">
@import 'style.less';
</style>

View File

@@ -0,0 +1,79 @@
.page {
box-sizing: border-box;
min-height: 100%;
//padding-bottom: 80px;
//margin-top: 81px;
}
.paydemo {
.content {
max-width: 1120px;
margin: 0 auto;
}
.paydemo-type-content {
padding: 20px;
margin-bottom: 10px;
background-color: #FFFFFF;
border-radius: 6px;
p {
margin-bottom: 10px;
text-align: left;
}
}
.colorChange {
color: #1953ff;
border: solid 2px #1953ff;
padding: 12px;
margin-right: 10px;
cursor: pointer;
}
.paydemoType {
padding: 12px;
border: solid 2px #e2e2e2;
margin-right: 10px;
cursor: pointer;
}
.paydemo-type-img {
width: 40px;
height: 40px;
vertical-align: center;
}
.paydemo-type-name {
font-size: 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.paydemo-type-body {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.paydemo-type-h5 {
padding: 12px;
border: solid 2px #e2e2e2;
margin-right: 10px;
cursor: pointer;
font-size: 14px;
}
.codeImg_wx_h5 {
position: absolute;
z-index: 1001;
display: flex;
flex-direction: column;
align-items: center;
background-color: #ffffff;
width: 160px;
}
.paydemo-form-item {
height: 38px;
margin-bottom: 5px;
display: flex;
flex-direction: row;
align-items: center;
}
}
img {
border: 0;
vertical-align: middle;
}