10 Commits

Author SHA1 Message Date
DaxPay
75a4af9727 refactor(env): 调整环境变量配置并更新依赖版本- 2024-12-10 20:12:59 +08:00
DaxPay
7eec0bec88 fix 微信聚合收银台报错 2024-12-05 15:39:29 +08:00
bootx
bd82469633 refactor(daxpay): 优化代码结构和类型定义
-调整代码格式和缩进,提高可读性
2024-12-04 22:43:07 +08:00
DaxPay
7f95a9bec3 refactor(daxpay): 重构收银台界面和支付流程- 更新了 AlipayAuth、WechatAuth、AlipayCashierCode、WechatCashierCode、AliAggregate、WechatAggregate 和 CheckoutPay 组件的结构和样式
- 优化了支付流程,增加了加载状态和错误处理
-调整了订单信息的展示方式,使其更加清晰和详细
- 统一了支付按钮的样式和行为
- 重构了部分 API 接口,增加了主键字段
2024-12-04 19:22:55 +08:00
DaxPay
db4a87d3ae refactor(daxpay): 重构收银台相关代码
- 修改枚举名称:将 AggregateTypeEnum重命名为 CheckoutAggregateEnum
- 更新路由配置:修改收银台相关页面的路由名称
- 优化支付宝和微信收银台页面:调整页面布局和支付逻辑
- 重构公共方法:统一处理订单和配置信息获取
-调整样式:优化收银台页面样式
2024-12-04 15:07:23 +08:00
bootx
42791aa2a6 feat(daxpay): 实现微信和支付宝聚合支付功能
- 新增 AliAggregate 和 WechatAggregate组件用于聚合支付页面
- 实现了获取聚合配置、认证、支付等核心逻辑
- 优化了错误处理和页面跳转逻辑
- 重构了部分 API接口和数据结构
2024-12-03 22:11:17 +08:00
DaxPay
ba00dbd561 feat(daxpay): 实现聚合支付功能
- 新增聚合支付相关枚举和接口
- 实现聚合支付的路由和页面组件
- 添加聚合支付的API请求和逻辑处理
- 优化普通支付的流程和界面
2024-12-03 17:38:27 +08:00
bootx
c022aa8650 refactor(daxpay): 重构支付宝聚合支付功能
- 更新 AliAggregate.vue 组件,适配新的聚合支付接口和数据结构
- 修改 CheckoutPay.api.ts,统一支付相关 API 接口定义
- 新增 AggregateTypeEnum 枚举,用于区分不同的聚合支付类型
2024-12-02 22:35:10 +08:00
DaxPay
85d0bd852c refactor(daxpay): 重构收银台相关代码
- 修改路由配置,统一使用 orderNo替代 appId 和 orderId
-重命名相关组件和文件,优化目录结构
- 更新错误提示信息,使其更加友好
- 新增 CheckoutPay.api.ts 文件,定义收银台相关 API 接口
2024-12-02 19:57:39 +08:00
DaxPay
2ab011adfc refactor(router): 重构收银台路由和组件
- 更新路由配置,统一收银台相关路径
- 重命名收银台相关组件,提高代码可读性
- 新增结算台相关路由和组件
- 优化微信和支付宝收银台的逻辑
- 调整收银台 API 接口和数据结构
2024-11-25 20:27:30 +08:00
19 changed files with 2051 additions and 1258 deletions

View File

@@ -1,12 +1,12 @@
# 独立部署模式
VITE_PUBLIC_PATH=
#VITE_PUBLIC_PATH=
# API 接口前缀
VITE_GLOB_API_URL_PREFIX=/api
#VITE_GLOB_API_URL_PREFIX=/api
# 嵌入式模式 与后端部署在一起
#VITE_PUBLIC_PATH=/h5
VITE_PUBLIC_PATH=/h5
# API 接口前缀
#VITE_GLOB_API_URL_PREFIX=
VITE_GLOB_API_URL_PREFIX=
# 是否删除console
VITE_DROP_CONSOLE=true

4
components.d.ts vendored
View File

@@ -11,12 +11,16 @@ declare module 'vue' {
Logo: typeof import('./src/components/Logo.vue')['default']
SvgIcon: typeof import('./src/components/SvgIcon.vue')['default']
VanButton: typeof import('vant/es')['Button']
VanCell: typeof import('vant/es')['Cell']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
VanDialog: typeof import('vant/es')['Dialog']
VanField: typeof import('vant/es')['Field']
VanLoading: typeof import('vant/es')['Loading']
VanNumberKeyboard: typeof import('vant/es')['NumberKeyboard']
VanOverlay: typeof import('vant/es')['Overlay']
VanSpace: typeof import('vant/es')['Space']
VanSubmitBar: typeof import('vant/es')['SubmitBar']
VanTextEllipsis: typeof import('vant/es')['TextEllipsis']
}
}

View File

@@ -27,62 +27,62 @@
"@types/lodash-es": "^4.17.12",
"@unocss/reset": "^0.58.9",
"@vueuse/core": "^10.11.1",
"axios": "^1.7.7",
"axios": "^1.7.9",
"date-fns": "^3.6.0",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0",
"pinia": "^2.2.2",
"pinia": "^2.3.0",
"pinia-plugin-persist": "^1.0.0",
"qs": "^6.13.0",
"vant": "^4.9.7",
"vue": "^3.5.8",
"qs": "^6.13.1",
"vant": "^4.9.10",
"vue": "^3.5.13",
"vue-router": "4.2.5"
},
"devDependencies": {
"@antfu/eslint-config": "^2.27.3",
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@iconify/json": "^2.2.252",
"@iconify/json": "^2.2.281",
"@types/fs-extra": "^11.0.4",
"@types/mockjs": "^1.0.10",
"@types/node": "^20.16.5",
"@types/node": "^20.17.9",
"@types/nprogress": "^0.2.3",
"@types/qs": "^6.9.16",
"@types/qs": "^6.9.17",
"@unocss/eslint-plugin": "^0.58.9",
"@unocss/preset-icons": "^0.58.9",
"@unocss/preset-rem-to-px": "^0.58.9",
"@unocss/transformer-directives": "^0.58.9",
"@unocss/transformer-variant-group": "^0.58.9",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue": "^5.2.1",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"cz-git": "^1.9.4",
"dotenv": "^16.4.5",
"cz-git": "^1.11.0",
"dotenv": "^16.4.7",
"eslint": "^8.57.1",
"eslint-plugin-format": "^0.1.2",
"eslint-plugin-format": "^0.1.3",
"esno": "^0.16.3",
"fs-extra": "^11.2.0",
"less": "^4.2.0",
"less": "^4.2.1",
"lint-staged": "^15.2.10",
"only-allow": "^1.2.1",
"picocolors": "^1.1.0",
"postcss": "^8.4.47",
"postcss-mobile-forever": "^4.1.6",
"picocolors": "^1.1.1",
"postcss": "^8.4.49",
"postcss-mobile-forever": "^4.3.1",
"rimraf": "^3.0.2",
"rollup": "^4.22.4",
"rollup": "^4.28.1",
"rollup-plugin-visualizer": "^5.12.0",
"simple-git-hooks": "^2.11.1",
"typescript": "^5.6.2",
"typescript": "^5.7.2",
"unocss": "^0.58.9",
"unplugin-auto-import": "^0.17.8",
"unplugin-vue-components": "^0.26.0",
"vconsole": "^3.15.1",
"vite-plugin-vconsole": "^2.1.1",
"vite": "5.4.6",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.2",
"vite-plugin-mock": "^2.9.8",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vconsole": "^2.1.1",
"vue-tsc": "^1.8.27"
},
"lint-staged": {

2440
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ export enum ChannelEnum {
/**
* 支付方式
*/
export enum payMethodEnum {
export enum PayMethodEnum {
WAP = 'wap',
APP = 'app',
WEB = 'web',
@@ -26,3 +26,21 @@ export enum CashierTypeEnum {
WECHAT_PAY = 'wechat_pay',
ALIPAY = 'alipay',
}
/**
* 收银台类型
*/
export enum CheckoutTypeEnum {
H5 = 'h5',
PC = 'pc',
MINI_APP = 'mini_app',
AGGREGATE = 'aggregate',
}
/**
* 收银台聚合支付类型
*/
export enum CheckoutAggregateEnum {
ALI = 'alipay',
WECHAT = 'wechat_pay',
}

View File

@@ -11,7 +11,7 @@ export const DaxPayRoute: RouteRecordRaw = {
component: Layout,
children: [
{
path: '/alipay/auth/:appId/:channel/:queryCode/:aliAppId',
path: '/auth/alipay/:appId/:channel/:queryCode/:aliAppId',
name: 'AlipayAuth',
component: () => import('@/views/daxpay/auth/alipay/AlipayAuth.vue'),
meta: {
@@ -19,7 +19,7 @@ export const DaxPayRoute: RouteRecordRaw = {
},
},
{
path: '/wechat/auth/:appId/:channel/:queryCode',
path: '/auth/wechat/:appId/:channel/:queryCode',
name: 'WechatAuth',
component: () => import('@/views/daxpay/auth/wechat/WechatAuth.vue'),
meta: {
@@ -27,28 +27,60 @@ export const DaxPayRoute: RouteRecordRaw = {
},
},
{
path: '/channel/cashier/:appId',
name: 'ChannelCashier',
component: () => import('@/views/daxpay/cashier/ChannelCashier.vue'),
path: '/cashier/:code',
name: 'CashierCode',
component: () => import('@/views/daxpay/cashier/CashierCode.vue'),
meta: {
title: '收银台',
},
},
{
path: '/alipay/cashier/:appId',
name: 'AlipayCashier',
component: () => import('@/views/daxpay/cashier/alipay/AlipayCashier.vue'),
path: '/cashier/alipay/:code',
name: 'AlipayCashierCode',
component: () => import('@/views/daxpay/cashier/alipay/AlipayCashierCode.vue'),
meta: {
title: '支付宝收银台',
},
},
{
path: '/wechat/cashier/:appId',
name: 'WechatCashier',
component: () => import('@/views/daxpay/cashier/wechat/WechatCashier.vue'),
path: '/cashier/wechat/:code',
name: 'WechatCashierCode',
component: () => import('@/views/daxpay/cashier/wechat/WechatCashierCode.vue'),
meta: {
title: '微信收银台',
},
},
{
path: '/checkout/:orderNo',
name: 'CheckoutPay',
component: () => import('@/views/daxpay/checkout/CheckoutPay.vue'),
meta: {
title: '手机收银台',
},
},
{
path: '/aggregate/:orderNo',
name: 'CheckAggregate',
component: () => import('@/views/daxpay/checkout/CheckAggregate.vue'),
meta: {
title: '手机收银台',
},
},
{
path: '/aggregate/alipay/:orderNo',
name: 'AliCheckout',
component: () => import('@/views/daxpay/checkout/alipay/AliAggregate.vue'),
meta: {
title: '支付宝收银台',
},
},
{
path: '/aggregate/wechat/:orderNo',
name: 'WechatCheckout',
component: () => import('@/views/daxpay/checkout/wechat/WechatAggregate.vue'),
meta: {
title: '微信结算台',
},
},
],
}

View File

@@ -9,3 +9,10 @@ body {
.van-action-sheet__cancel {
color: var(--van-primary-color) !important;
}
.loading-wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}

View File

@@ -1,6 +1,6 @@
<template>
<van-overlay v-show="show" :show="true">
<div class="wrapper" @click.stop>
<div class="loading-wrapper" @click.stop>
<van-loading size="24px">
获取中...
</van-loading>
@@ -60,12 +60,6 @@ async function init() {
</script>
<style scoped lang="less">
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.block {
width: 120px;

View File

@@ -1,6 +1,6 @@
<template>
<van-overlay v-show="show" :show="true">
<div class="wrapper" @click.stop>
<div class="loading-wrapper" @click.stop>
<van-loading size="24px">
获取中...
</van-loading>
@@ -47,12 +47,6 @@ async function init() {
</script>
<style scoped lang="less">
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.block {
width: 120px;

View File

@@ -2,24 +2,23 @@ import type { AuthResult } from '../auth/ChannelAuth.api'
import { http } from '@/utils/http/axios'
import type { Result } from '#/axios'
/**
*
*
*/
export function getCashierInfo(cashierType: string, appId: string) {
return http.request<Result<ChannelCashierConfigResult>>({
url: '/unipay/ext/channel/cashier/getCashier',
export function getCashierTypeConfig(cashierType: string, cashierCode: string) {
return http.request<Result<CashierTypeConfigResult>>({
url: '/unipay/cashier/code/findByCashierType',
method: 'GET',
params: { cashierType, appId },
params: { cashierType, cashierCode },
})
}
/**
* , OpenId一类的信息
*/
export function generateAuthUrl(param: CashierAuthParam) {
export function generateAuthUrl(param: CashierAuthUrlParam) {
return http.request<Result<string>>({
url: '/unipay/ext/channel/cashier/generateAuthUrl',
url: '/unipay/cashier/code/generateAuthUrl',
method: 'POST',
data: param,
})
@@ -28,9 +27,9 @@ export function generateAuthUrl(param: CashierAuthParam) {
/**
*
*/
export function auth(param: CashierPayParam) {
export function auth(param: CashierAuthParam) {
return http.request<Result<AuthResult>>({
url: '/unipay/ext/channel/cashier/auth',
url: '/unipay/cashier/code/auth',
method: 'POST',
data: param,
})
@@ -41,18 +40,28 @@ export function auth(param: CashierPayParam) {
*/
export function cashierPay(param: CashierPayParam) {
return http.request<Result<PayResult>>({
url: '/unipay/ext/channel/cashier/pay',
url: '/unipay/cashier/code/pay',
method: 'POST',
data: param,
})
}
/**
*
* URL参
*/
export interface CashierAuthUrlParam {
// 应用号
cashierCode?: string
// 收银台类型
cashierType?: string
}
/**
*
*/
export interface CashierAuthParam {
// 应用号
appId?: string
cashierCode?: string
// 收银台类型
cashierType?: string
// 授权码
@@ -64,7 +73,7 @@ export interface CashierAuthParam {
*/
export interface CashierPayParam {
// 应用号
appId?: string
cashierCode?: string
// 收银台类型
cashierType?: string
// 支付金额
@@ -79,7 +88,6 @@ export interface CashierPayParam {
*
*/
export interface PayResult {
// 支付状态
status: string
// 支付参数体
@@ -109,21 +117,17 @@ export interface WxJsapiSignResult {
}
/**
*
*
*/
export interface ChannelCashierConfigResult {
export interface CashierTypeConfigResult {
// 应用号
appId?: string
// 收银台类型
cashierType?: string
// 收银台名称
cashierName?: string
// 码牌名称
name?: string
// 支付通道
channel?: string
// 支付方式
payMethod?: string
// 是否开启分账
allocation?: boolean
// 自动分账
autoAllocation?: boolean
}

View File

@@ -6,17 +6,17 @@ import { useRoute } from 'vue-router'
import router from '@/router'
const route = useRoute()
const { appId } = route.params
const { code } = route.params
const ua = navigator.userAgent
if (ua.includes('MicroMessenger')) {
router.push({ path: `/wechat/cashier/${appId}`, replace: true })
router.push({ path: `/cashier/wechat/${code}`, replace: true })
}
else if (ua.includes('Alipay')) {
router.push({ path: `/alipay/cashier/${appId}`, replace: true })
router.push({ path: `/cashier/alipay/${code}`, replace: true })
}
else {
router.push({ name: 'ErrorResult', query: { msg: '请使用支付宝、微信等支付程序进行扫码支付' }, replace: true })
router.push({ name: 'ErrorResult', query: { msg: '请使用支付宝、微信等软件进行扫码支付' }, replace: true })
}
</script>

View File

@@ -2,7 +2,7 @@
<div>
<div class="container">
<div style="font-size: 28px;margin-top: 10px;">
{{ cashierInfo.cashierName || '支付宝收银台' }}
{{ cashierTypeConfig.name || '支付宝收银台' }}
</div>
<div class="amount-display">
<p style="font-size: 20px">
@@ -65,23 +65,23 @@ import { useRoute } from 'vue-router'
import { showNotify } from 'vant'
import type {
CashierPayParam,
ChannelCashierConfigResult,
} from '@/views/daxpay/cashier/ChannelCashier.api'
CashierTypeConfigResult,
} from '@/views/daxpay/cashier/CashierCode.api'
import {
cashierPay,
getCashierInfo,
} from '@/views/daxpay/cashier/ChannelCashier.api'
getCashierTypeConfig,
} from '@/views/daxpay/cashier/CashierCode.api'
import { CashierTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
import { useKeyboard } from '@/hooks/daxpay/useKeyboard'
const route = useRoute()
const { appId } = route.params
const { code } = route.params
const showRemark = ref<boolean>(false)
const loading = ref<boolean>(false)
const cashierInfo = ref<ChannelCashierConfigResult>({})
const cashierTypeConfig = ref<CashierTypeConfigResult>({})
const amount = ref<string>('0')
const description = ref<string>('')
@@ -95,10 +95,10 @@ onMounted(() => {
* 初始化数据
*/
function initData() {
getCashierInfo(CashierTypeEnum.ALIPAY, appId as string).then(({ data }) => {
cashierInfo.value = data
getCashierTypeConfig(CashierTypeEnum.ALIPAY, code as string).then(({ data }) => {
cashierTypeConfig.value = data
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message } })
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
}
@@ -114,7 +114,7 @@ function pay() {
loading.value = true
const from = {
amount: amountValue,
appId,
cashierCode: code,
cashierType: CashierTypeEnum.ALIPAY,
description: description.value,
} as CashierPayParam

View File

@@ -2,7 +2,7 @@
<div v-if="show">
<div class="container">
<div style="font-size: 28px;margin-top: 10px;">
{{ cashierInfo.cashierName || '微信收银台' }}
{{ cashierTypeConfig.name || '微信收银台' }}
</div>
<div class="amount-display">
<p style="font-size: 20px">
@@ -66,38 +66,37 @@ import { showNotify } from 'vant'
import type {
CashierAuthParam,
CashierPayParam,
ChannelCashierConfigResult,
CashierTypeConfigResult,
WxJsapiSignResult,
} from '@/views/daxpay/cashier/ChannelCashier.api'
} from '@/views/daxpay/cashier/CashierCode.api'
import {
auth
, cashierPay,
generateAuthUrl,
getCashierInfo,
} from '@/views/daxpay/cashier/ChannelCashier.api'
getCashierTypeConfig,
} from '@/views/daxpay/cashier/CashierCode.api'
import { CashierTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
import { useKeyboard } from '@/hooks/daxpay/useKeyboard'
const route = useRoute()
const { appId } = route.params
const { code } = route.query
const { code } = route.params
const { code: authCode } = route.query
const show = ref<boolean>(false)
const showRemark = ref<boolean>(false)
const loading = ref<boolean>(false)
const cashierInfo = ref<ChannelCashierConfigResult>({})
const cashierTypeConfig = ref<CashierTypeConfigResult>({})
const amount = ref<string>('0')
const description = ref<string>('')
const openId = ref<string>('')
//
const authParam = ref<CashierAuthParam>({
appId: appId as string,
cashierCode: code as string,
cashierType: CashierTypeEnum.WECHAT_PAY,
})
const { input, del } = useKeyboard(amount)
onMounted(() => {
@@ -105,21 +104,21 @@ onMounted(() => {
})
/**
* 初始化
* 进入页面的初始化
*/
function init() {
//
if (!code) {
//
if (!authCode) {
//
generateAuthUrl(authParam.value).then((res) => {
generateAuthUrl({ cashierType: CashierTypeEnum.WECHAT_PAY, cashierCode: code as string }).then((res) => {
const url = res.data
location.replace(url)
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message } })
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
}
else {
authParam.value.authCode = code as string
authParam.value.authCode = authCode as string
//
initData()
}
@@ -130,15 +129,18 @@ function init() {
*/
function initData() {
show.value = true
getCashierInfo(CashierTypeEnum.ALIPAY, appId as string).then(({ data }) => {
cashierInfo.value = data
//
getCashierTypeConfig(CashierTypeEnum.WECHAT_PAY, code as string).then(({ data }) => {
cashierTypeConfig.value = data
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message } })
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
// OpenId
auth(authParam.value).then(({ data }) => {
openId.value = data.openId as string
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message } })
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
}
@@ -154,8 +156,8 @@ function pay() {
loading.value = true
const from = {
cashierCode: code,
amount: amountValue,
appId,
openId: openId.value,
cashierType: CashierTypeEnum.WECHAT_PAY,
description: description.value,

View File

@@ -0,0 +1,24 @@
<template>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
import router from '@/router'
const route = useRoute()
const { orderNo } = route.params
const ua = navigator.userAgent
if (ua.includes('MicroMessenger')) {
router.push({ path: `/aggregate/wechat/${orderNo}`, replace: true })
}
else if (ua.includes('Alipay')) {
router.push({ path: `/aggregate/alipay/${orderNo}`, replace: true })
}
else {
router.push({ name: 'ErrorResult', query: { msg: '请使用支付宝、微信等软件进行扫码支付' }, replace: true })
}
</script>
<style scoped lang="less">
</style>

View File

@@ -0,0 +1,213 @@
import { http } from '@/utils/http/axios'
import type { Result } from '#/axios'
import type { AuthResult } from '@/views/daxpay/auth/ChannelAuth.api'
import type { PayResult } from '@/views/daxpay/cashier/CashierCode.api'
/**
* 获取收银台订单和配置信息
*/
export function getOrderAndConfig(orderNo, checkoutType) {
return http.request<Result<CheckoutOrderAndConfigResult>>({
url: '/unipay/checkout/getOrderAndConfig',
method: 'GET',
params: {
orderNo,
checkoutType,
},
})
}
/**
* 获取聚合支付配置
*/
export function getAggregateConfig(orderNo, aggregateType) {
return http.request<Result<AggregateOrderAndConfigResult>>({
url: '/unipay/checkout/getAggregateConfig',
method: 'GET',
params: {
orderNo,
aggregateType,
},
})
}
/**
* 获取收银台所需授权链接, 用于获取OpenId一类的信息
*/
export function generateAuthUrl(param: CheckoutAuthUrlParam) {
return http.request<Result<string>>({
url: '/unipay/checkout/generateAuthUrl',
method: 'post',
data: param,
})
}
/**
* 获取授权结果
*/
export function auth(param: CheckoutAuthCodeParam) {
return http.request<Result<AuthResult>>({
url: '/unipay/checkout/auth',
method: 'post',
data: param,
})
}
/**
* 发起普通支付
*/
export function checkoutPay(param: CheckoutPayParam) {
return http.request<Result<PayResult>>({
url: '/unipay/checkout/pay',
method: 'post',
data: param,
})
}
/**
* 发起聚合支付
*/
export function aggregatePay(param: CheckoutAggregatePayParam) {
return http.request<Result<PayResult>>({
url: '/unipay/checkout/aggregatePay',
method: 'post',
data: param,
})
}
/**
* 收银台认证链接生成参数
*/
export interface CheckoutAuthUrlParam {
/** 要支付的订单号 */
orderNo?: string
/** 聚合支付类型 */
aggregateType?: string
}
/**
* 获取收银台认证结果参数
*/
export interface CheckoutAuthCodeParam {
/** 要支付的订单号 */
orderNo?: string
/** 聚合支付类型 */
aggregateType?: string
/** 认证Code */
authCode?: string
}
/**
* 收银台支付参数
*/
export interface CheckoutPayParam {
/** 订单号 */
orderNo?: string
/** 支付配置项ID */
itemId?: string
/** 唯一标识 */
openId?: string
/** 付款码 */
barCode?: string
}
/**
* 聚合支付参数
*/
export interface CheckoutAggregatePayParam {
/** 订单号 */
orderNo?: string
/** 聚合支付类型 */
aggregateType?: string
/** 唯一标识 */
openId?: 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?: number
}
/**
* 收银台配置信息
*/
export interface CheckoutConfigResult {
/** 收银台名称 */
name?: string
/** PC收银台是否同时显示聚合收银码 */
aggregateShow?: boolean
/** h5收银台自动升级聚合支付 */
h5AutoUpgrade?: boolean
}
/**
* 收银台分类配置
*/
export interface CheckoutGroupConfigResult {
/** 主键 */
id?: 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,118 @@
<template>
<div v-if="show">
<van-overlay :show="loading">
<div class="loading-wrapper" @click.stop>
<van-loading size="24px">
支付中...
</van-loading>
</div>
</van-overlay>
<van-cell-group inset title="订单信息">
<van-cell title="金额" title-style="font-size: 22px;color: red">
<template #default>
<span style="font-size: 22px;color: red">{{ orderAndConfig.order.amount }}</span>
</template>
</van-cell>
<van-field label="标题" :model-value="orderAndConfig.order.title" readonly />
<van-field label="订单号" :model-value="orderAndConfig.order.bizOrderNo" readonly />
<van-field label="支付号" :model-value="orderAndConfig.order.orderNo" readonly />
<van-field label="描述" rows="2" type="textarea" :model-value="orderAndConfig.order.description" readonly />
</van-cell-group>
<van-cell-group v-for="group in orderAndConfig.groupConfigs" :key="group.id" inset :title="group.name">
<van-space direction="vertical" fill>
<van-button v-for="config in group.items" :key="config.id" type="primary" block @click="pay(config)">
{{ config.name }}
</van-button>
</van-space>
</van-cell-group>
</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { onMounted, ref } from 'vue'
import type {
CheckoutItemConfigResult,
CheckoutOrderAndConfigResult,
CheckoutPayParam,
} from './CheckoutPay.api'
import {
checkoutPay
, getOrderAndConfig,
} from './CheckoutPay.api'
import { CheckoutTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
const route = useRoute()
const { orderNo } = route.params
const ua = navigator.userAgent
const show = ref(false)
const loading = ref(false)
const orderAndConfig = ref<CheckoutOrderAndConfigResult>({
order: {},
config: {},
groupConfigs: [],
})
/**
* 初始化
*/
onMounted(() => {
initData()
})
/**
* 初始化数据
*/
async function initData() {
// 获取收银台配置
await getOrderAndConfig(orderNo, CheckoutTypeEnum.H5).then(({ data }) => {
orderAndConfig.value = data
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
// 判断是否自动升级为聚合控制台
if (orderAndConfig.value.config.h5AutoUpgrade) {
goAggregate()
return
}
show.value = true
}
/**
* 跳转到聚合收银台
*/
function goAggregate() {
if (ua.includes('MicroMessenger')) {
router.push({ path: `/aggregate/wechat/${orderNo}`, replace: true })
}
else if (ua.includes('Alipay')) {
router.push({ path: `/aggregate/alipay/${orderNo}`, replace: true })
}
else {
show.value = true
}
}
/**
* 发起支付
*/
function pay(config: CheckoutItemConfigResult) {
loading.value = true
const from = {
orderNo,
itemId: config.id,
} as CheckoutPayParam
checkoutPay(from).then(({ data }) => {
loading.value = false
// 跳转到支付页面
location.replace(data.payBody)
})
}
</script>
<style scoped lang="less">
</style>

View File

@@ -0,0 +1,89 @@
<template>
<div>
<van-overlay :show="loading">
<div class="loading-wrapper" @click.stop>
<van-loading size="24px">
支付中...
</van-loading>
</div>
</van-overlay>
<van-cell-group inset title="订单信息">
<van-cell title="金额" title-style="font-size: 22px;color: red">
<template #default>
<span style="font-size: 22px;color: red">{{ aggregateInfo.order.amount }}</span>
</template>
</van-cell>
<van-field label="标题" :model-value="aggregateInfo.order.title" readonly />
<van-field label="订单号" :model-value="aggregateInfo.order.bizOrderNo" readonly />
<van-field label="支付号" :model-value="aggregateInfo.order.orderNo" readonly />
<van-field label="描述" rows="2" type="textarea" :model-value="aggregateInfo.order.description" readonly />
</van-cell-group>
<van-submit-bar safe-area-inset-bottom :price="(aggregateInfo.order.amount || 0)*100" button-text="支付" @submit="pay" />
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { CheckoutAggregateEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
import type {
AggregateOrderAndConfigResult,
CheckoutAggregatePayParam,
} from '@/views/daxpay/checkout/CheckoutPay.api'
import {
aggregatePay,
getAggregateConfig,
} from '@/views/daxpay/checkout/CheckoutPay.api'
const route = useRoute()
const { orderNo } = route.params
const loading = ref<boolean>(false)
const aggregateInfo = ref<AggregateOrderAndConfigResult>({
aggregateConfig: {},
config: {},
order: {},
})
onMounted(() => {
initData()
})
/**
* 初始化数据
*/
async function initData() {
// 查询订单和配置
await getAggregateConfig(orderNo, CheckoutAggregateEnum.ALI).then(({ data }) => {
aggregateInfo.value = data
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
// 判断是否自动拉起支付
if (aggregateInfo.value.aggregateConfig.autoLaunch) {
pay()
}
}
/**
* 支付
*/
function pay() {
loading.value = true
const from = {
orderNo: aggregateInfo.value.order.orderNo,
aggregateType: CheckoutAggregateEnum.ALI,
} as CheckoutAggregatePayParam
aggregatePay(from)
.then(({ data }) => {
loading.value = false
// 跳转到H5/扫码支付页面
location.replace(data.payBody)
})
}
</script>
<style scoped lang="less">
</style>

View File

@@ -0,0 +1,152 @@
<template>
<div v-if="show">
<van-overlay :show="loading">
<div class="loading-wrapper" @click.stop>
<van-loading size="24px">
支付中...
</van-loading>
</div>
</van-overlay>
<van-cell-group inset>
<van-cell title="金额" title-style="font-size: 22px;color: red">
<template #default>
<span style="font-size: 22px;color: red">{{ aggregateInfo.order.amount }}</span>
</template>
</van-cell>
<van-field label="标题" :model-value="aggregateInfo.order.title" readonly />
<van-field label="订单号" :model-value="aggregateInfo.order.bizOrderNo" readonly />
<van-field label="支付号" :model-value="aggregateInfo.order.orderNo" readonly />
<van-field label="描述" rows="2" type="textarea" :model-value="aggregateInfo.order.description" readonly />
</van-cell-group>
<van-submit-bar safe-area-inset-bottom :price="(aggregateInfo.order.amount || 0) * 100" button-text="支付" @submit="pay" />
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { CheckoutAggregateEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
import type {
AggregateOrderAndConfigResult, CheckoutAggregatePayParam,
CheckoutAuthCodeParam} from '@/views/daxpay/checkout/CheckoutPay.api';
import { aggregatePay,
auth, generateAuthUrl, getAggregateConfig } from '@/views/daxpay/checkout/CheckoutPay.api'
import type { WxJsapiSignResult } from '@/views/daxpay/cashier/CashierCode.api'
const route = useRoute()
const { orderNo } = route.params
const { code: authCode } = route.query
const show = ref<boolean>(false)
const loading = ref<boolean>(false)
const openId = ref<string>('')
// 认证参数
const authParam = ref<CheckoutAuthCodeParam>({
orderNo: orderNo as string,
aggregateType: CheckoutAggregateEnum.WECHAT,
})
const aggregateInfo = ref<AggregateOrderAndConfigResult>({
aggregateConfig: {},
config: {},
order: {},
})
onMounted(() => {
init()
})
/**
* 进入页面的初始化
*/
function init() {
// 如果不是重定向跳转过来, 跳转到到重定向授权地址
if (!authCode) {
// 重定向跳转到微信授权地址
generateAuthUrl({ orderNo: orderNo as string, aggregateType: CheckoutAggregateEnum.WECHAT }).then((res) => {
const url = res.data
location.replace(url)
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
}
else {
authParam.value.authCode = authCode as string
// 初始化数据
initData()
}
}
/**
* 初始化数据
*/
async function initData() {
show.value = true
// 获取聚合配置
getAggregateConfig(orderNo, CheckoutAggregateEnum.WECHAT).then(({ data }) => {
aggregateInfo.value = data
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
// 认证获取OpenId
await auth(authParam.value).then(({ data }) => {
openId.value = data.openId as string
}).catch((res) => {
router.push({ name: 'ErrorResult', query: { msg: res.message }, replace: true })
})
// 判断是否自动拉起支付
if (aggregateInfo.value.aggregateConfig.autoLaunch) {
pay()
}
}
/**
* 微信jsapi方式支付
*/
function pay() {
loading.value = true
const from = {
orderNo: orderNo as string,
aggregateType: CheckoutAggregateEnum.WECHAT,
openId: openId.value,
} as CheckoutAggregatePayParam
aggregatePay(from)
.then(({ data }) => {
loading.value = false
// 拉起jsapi支付
const json = JSON.parse(data.payBody)
jsapiPay(json)
})
}
/**
* 拉起Jsapi支付窗口
*/
function jsapiPay(data: WxJsapiSignResult) {
const form = {
appId: data.appId, // 公众号ID由商户传入
timeStamp: data.timeStamp, // 时间戳自1970年以来的秒数
nonceStr: data.nonceStr, // 随机串
package: data.package, // 预支付ID
signType: data.signType, // 微信签名方式:
paySign: data.paySign, // 微信签名
}
// 使用微信JsSdk拉起支付
WeixinJSBridge.invoke('getBrandWCPayRequest', form, (res) => {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
// 跳转到成功页面
router.push({ name: 'SuccessResult', query: { msg: '支付成功' }, replace: true })
}
})
}
</script>
<style scoped lang="less">
</style>

View File

@@ -303,6 +303,6 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}