Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	src/views/daxpay/h5/code/CashierCode.api.ts
#	src/views/daxpay/h5/code/alipay/AlipayCashierCode.vue
#	src/views/daxpay/h5/code/wechat/WechatCashierCode.vue
This commit is contained in:
bootx
2025-11-11 16:20:01 +08:00
3 changed files with 162 additions and 297 deletions

View File

@@ -5,22 +5,22 @@ import type { AuthResult } from '@/views/daxpay/h5/auth/ChannelAuth.api'
/**
* 获取码牌配置
*/
export function getCashierCodeConfig(code, scene) {
return http.request<Result<GatewayCashierCodeConfig>>({
url: '/unipay/gateway/cashier/code/getCodeConfig',
export function getCashierCodeConfig(cashierCode, scene) {
return http.request<Result<GatewayCashierConfig>>({
url: '/unipay/gateway/cashier/getCodeConfig',
method: 'GET',
params: { code, scene },
params: { cashierCode, scene },
})
}
/**
* 获取码牌收银台所需授权链接, 用于获取OpenId一类的信息
*/
export function generateAuthUrl(code, scene) {
export function generateAuthUrl(cashierCode, cashierScene) {
return http.request<Result<string>>({
url: '/unipay/gateway/cashier/code/generateAuthUrl',
method: 'POST',
data: { code, scene },
data: { cashierCode, cashierScene },
})
}
@@ -38,7 +38,7 @@ export function auth(param: CashierCodeAuthParam) {
/**
* 发起支付
*/
export function cashierPay(param: GatewayCashierCodePayParam) {
export function cashierPay(param: CashierPayParam) {
return http.request<Result<PayResult>>({
url: '/unipay/gateway/cashier/code/pay',
method: 'POST',
@@ -49,11 +49,11 @@ export function cashierPay(param: GatewayCashierCodePayParam) {
/**
* 码牌收银支付参数
*/
export interface GatewayCashierCodePayParam {
export interface CashierPayParam {
// 收银码牌编码
code?: string
cashierCode?: string
// 收银台类型
scene?: string
cashierScene?: string
// 支付金额
amount?: number
// 唯一标识
@@ -95,22 +95,14 @@ export interface WxJsapiSignResult {
}
/**
* 码牌配置信息
* 收银台配置信息
*/
export interface GatewayCashierCodeConfig {
// 码牌名称
export interface GatewayCashierConfig {
// 商家名称
name?: string
// 是否启用
enable?: boolean
// 金额类型
amountType?: string
// 金额
amount?: string
// 支付通道
channel?: string
// 支付调起方式
callType?: string
// 判断是否需要获取openId
// 判断是否需要
needOpenId?: boolean
// 备注
remark?: string
@@ -120,10 +112,10 @@ export interface GatewayCashierCodeConfig {
* 码牌用户标识认证类
*/
export interface CashierCodeAuthParam {
// 收银码牌编码
code?: string
// 码牌编码
cashierCode?: string
// 支付场景
scene?: string
cashierScene?: string
// 授权码
authCode?: string
authCode: string
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="mpPay">
<div>
<div class="container">
<div class="payName">
<span>付款给</span>
@@ -20,12 +20,7 @@
添加备注
</div>
<div v-else style="max-width: 75vw">
<p style="text-align: center;">
备注
</p>
<p class="remarkDecript">
{{ description }}
</p>
<van-text-ellipsis :content="`备注: ${description}`" />
<div />
</div>
</div>
@@ -61,7 +56,6 @@
/>
</van-dialog>
<van-number-keyboard
v-if="cashierInfo.amountType === 'random'"
:show="!loading"
theme="custom"
extra-key="."
@@ -70,11 +64,6 @@
@input="input"
@delete="del"
/>
<div v-if="cashierInfo.amountType === 'fixed'" class="redirectPayBtn" @click="pay">
<van-button type="primary">
付款
</van-button>
</div>
</div>
</template>
@@ -82,9 +71,9 @@
import { onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { showNotify } from 'vant'
import type { GatewayCashierCodeConfig, GatewayCashierCodePayParam } from '../CashierCode.api'
import type { CashierPayParam, GatewayCashierConfig } from '../CashierCode.api'
import { cashierPay, getCashierCodeConfig } from '../CashierCode.api'
import { AggregateEnum, CashierSceneEnum } from '@/enums/daxpay/DaxPayEnum'
import { AggregateEnum, CashierCodeTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import { useKeyboard } from '@/hooks/daxpay/useKeyboard'
const route = useRoute()
@@ -93,7 +82,7 @@ const { code } = route.params
const showRemark = ref<boolean>(false) // 是否展示备注
const loading = ref<boolean>(false) // 加载状态
const cashierInfo = ref<GatewayCashierCodeConfig>({})
const cashierInfo = ref<GatewayCashierConfig>()
const amount = ref<string>('0') // 金额
const description = ref<string>()
@@ -109,25 +98,14 @@ onMounted(() => {
function initData() {
// 获取信息
loading.value = true
getCashierCodeConfig(code, CashierSceneEnum.ALIPAY)
getCashierCodeConfig(code, CashierCodeTypeEnum.ALIPAY)
.then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
cashierInfo.value = res.data as any
// 是否启用
if (!cashierInfo.value?.enable) {
router.replace({ name: 'payFail', query: { msg: '收银码牌未启用' } })
}
// 判断类型
if (cashierInfo.value?.amountType === 'fixed') {
amount.value = res.data.amount as string
}
else {
amount.value = '0'
}
loading.value = false
cashierInfo.value = res.data as any
})
.catch((res) => {
router.replace({ name: 'payFail', query: { msg: res.message } })
@@ -146,10 +124,10 @@ function pay() {
loading.value = true
const from = {
amount: amountValue,
code,
cashierCode: code,
scene: AggregateEnum.ALI,
description: description.value,
} as GatewayCashierCodePayParam
} as CashierPayParam
cashierPay(from).then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
@@ -169,79 +147,58 @@ function pay() {
background: @color;
}
.mpPay {
position: relative;
.container {
background: linear-gradient(to bottom, #1e90ff, #1e90ff, #ffffff); // 从蓝色渐变到白色
width: 100%;
padding: 40px;
height: 40%;
border-radius: 10px;
text-align: center;
color: white;
.container {
background: linear-gradient(to bottom, #1e90ff, #1e90ff, #ffffff); // 从蓝色渐变到白色
width: 100%;
padding: 40px;
height: 40%;
border-radius: 10px;
text-align: center;
color: white;
.payName {
margin: 5px 0;
font-size: 16px;
.payName {
margin: 5px 0;
font-size: 16px;
.paytext {
font-size: 20px;
margin-left: 5px;
font-weight: 600;
}
}
.amount-display {
background-color: white;
color: @color;
border-radius: 20px;
padding: 20px;
margin: 20px 0;
display: flex;
gap: 1.875rem;
align-items: center;
p {
font-size: 32px;
}
.title {
font-size: 20px;
}
.paytext {
font-size: 20px;
margin-left: 5px;
font-weight: 600;
}
}
.notes {
width: 100%;
height: 10%;
.amount-display {
background-color: white;
color: @color;
border-radius: 20px;
padding: 20px;
margin: 20px 0;
display: flex;
justify-content: center;
gap: 1.875rem;
align-items: center;
.remark {
color: @color;
cursor: pointer;
.remarkDecript {
max-width: 75vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
p {
font-size: 32px;
}
}
.redirectPayBtn {
width: 100%;
margin: 0 auto;
position: absolute;
bottom: 3.125rem;
display: flex;
justify-content: center;
.van-button {
width: 90%;
.title {
font-size: 20px;
}
}
}
.notes {
width: 100%;
height: 10%;
display: flex;
justify-content: center;
align-items: center;
.remark {
color: @color;
cursor: pointer;
}
}
/* loading */
.loadingMask {
position: fixed;

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="show" class="wchatMpPay">
<div v-if="show">
<div class="container">
<div class="payName">
<span>付款给</span>
@@ -20,12 +20,7 @@
添加备注
</div>
<div v-else style="max-width: 75vw">
<p style="text-align: center;">
备注
</p>
<p class="remarkDecript">
{{ description }}
</p>
<van-text-ellipsis :content="`备注: ${description}`" />
<div />
</div>
</div>
@@ -61,7 +56,6 @@
/>
</van-dialog>
<van-number-keyboard
v-if="cashierInfo.amountType === 'random'"
:show="!loading"
theme="custom"
extra-key="."
@@ -70,11 +64,6 @@
@input="input"
@delete="del"
/>
<div v-if="cashierInfo.amountType === 'fixed'" class="redirectPayBtn" @click="pay">
<van-button type="success">
付款
</van-button>
</div>
</div>
</template>
@@ -83,11 +72,11 @@ import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { showNotify } from 'vant'
import type {
CashierCodeAuthParam,
GatewayCashierCodeConfig,
GatewayCashierCodePayParam,
CashierPayParam,
GatewayCashierConfig,
WxJsapiSignResult,
} from '../CashierCode.api'
import {
auth,
cashierPay,
@@ -95,18 +84,18 @@ import {
getCashierCodeConfig,
} from '../CashierCode.api'
import { AggregateEnum, CashierSceneEnum, GatewayCallTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import { AggregateEnum, CashierCodeTypeEnum, GatewayCallTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
import { useKeyboard } from '@/hooks/daxpay/useKeyboard'
import { getChannelAuthResult } from '@/utils/channelAuthUtil'
const route = useRoute()
const { channelAuth: isChannel, channel, code: cashierCode } = route.params
const { code: cashierCode } = 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<GatewayCashierCodeConfig>({})
const cashierInfo = ref<GatewayCashierConfig>({})
const amount = ref<string>('0')
const description = ref<string>()
const openId = ref<string>()
@@ -117,19 +106,13 @@ onMounted(() => {
init()
})
// 认证参数
const authParam = reactive<CashierCodeAuthParam>({
code: cashierCode as string,
scene: CashierSceneEnum.WECHAT_PAY,
})
/**
* 初始化
*/
async function init() {
loading.value = true
getCashierCodeConfig(cashierCode, AggregateEnum.WECHAT)
.then(async (res) => {
.then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
@@ -137,95 +120,48 @@ async function init() {
loading.value = false
const data = res.data
cashierInfo.value = data as any
// 判断码牌类型
if (cashierInfo.value?.amountType === 'fixed') {
amount.value = data.amount as string
}
else {
amount.value = '0'
}
// 判断是否需要获取OpenId
if (data.needOpenId) {
// 不等于9说明是微信重定向过来的
if (isChannel !== '9') {
if (isChannel === '1') {
// 通道认证
await channelAuth()
return
}
else {
// 微信官方认证
await commonAuth()
return
}
}
// 生成认证链接
generateAuthUrl(cashierCode, CashierSceneEnum.WECHAT_PAY)
.then((result) => {
if (res.code !== 0) {
// 如果异常,跳转异常页面
router.replace({
name: 'payFail',
query: { msg: result.msg },
})
// 如果不是重定向跳转过来, 跳转到到重定向地址
if (!authCode) {
// 重定向跳转到微信授权地址
generateAuthUrl(cashierCode, CashierCodeTypeEnum.WECHAT_PAY).then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
location.replace(result.data)
const url = res.data
location.replace(url)
}).catch((res) => {
router.replace({ name: 'payFail', query: { msg: res.message } })
})
.catch((result) => {
router.replace({
name: 'payFail',
query: { msg: result.message },
}
else {
// 认证获取OpenId
auth({ cashierCode: cashierCode as string, scene: CashierCodeTypeEnum.WECHAT_PAY, authCode: authCode as string })
.then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
openId.value = res.data.openId as string
show.value = true
}).catch((res) => {
router.replace({ name: 'payFail', query: { msg: res.message }, replace: true })
})
})
}
}
else {
show.value = true
}
})
}
/**
* 微信官方认证
*/
async function commonAuth() {
// 认证获取OpenId
const { code: authCode } = route.query
authParam.authCode = authCode as string
await wxAuth()
}
/**
* 通道微信认证
*/
async function channelAuth() {
// 获取回调的参数结果
const { openId } = getChannelAuthResult(channel as string, route.query)
authParam.authCode = openId
await wxAuth()
}
/**
* 认证操作
*/
async function wxAuth() {
await auth(authParam)
.then(({ data, code, msg }) => {
if (code) {
router.replace({ name: 'payFail', query: { msg }, replace: true })
return
}
openId.value = data.openId as string
show.value = true
})
.catch((res) => {
router.replace({ name: 'payFail', query: { msg: res.message }, replace: true })
.catch((error) => {
router.replace({ name: 'payFail', query: { msg: error } })
})
}
/**
* 调起支付, 需要根据调用类型发起
* 微信jsapi方式支付
*/
function pay() {
const amountValue = Number(amount.value)
@@ -239,31 +175,31 @@ function pay() {
amount: amountValue,
cashierCode,
openId: openId.value,
cashierScene: CashierSceneEnum.WECHAT_PAY,
scene: CashierCodeTypeEnum.WECHAT_PAY,
description: description.value,
} as GatewayCashierCodePayParam
cashierPay(from).then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
loading.value = false
// 根据类型拉起对应的支付。 支持跳转和jsapi
if (cashierInfo.value?.callType === GatewayCallTypeEnum.jsapi) {
const json = JSON.parse(res.data.payBody)
jsapiPay(json)
}
if (cashierInfo.value?.callType === GatewayCallTypeEnum.link) {
location.replace(res.data.payBody as any)
}
})
} as CashierPayParam
cashierPay(from)
.then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
loading.value = false
// 根据类型拉起对应的支付。 支持跳转和jsapi
if (cashierInfo.value?.callType === GatewayCallTypeEnum.jsapi) {
const json = JSON.parse(res.data.payBody)
jsapiPay(json)
}
if (cashierInfo.value?.callType === GatewayCallTypeEnum.link) {
location.replace(res.data.payBody as any)
}
})
}
/**
* 拉起Jsapi支付窗口
*/
function jsapiPay(data: WxJsapiSignResult) {
console.log(data)
const form = {
appId: data.appId, // 公众号ID由商户传入
timeStamp: data.timeStamp, // 时间戳自1970年以来的秒数
@@ -277,7 +213,7 @@ function jsapiPay(data: WxJsapiSignResult) {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
// 跳转到成功页面
router.replace({
name: 'PaySuccess',
path: '/paySuccess',
query: { title: '支付成功' },
})
}
@@ -291,76 +227,56 @@ function jsapiPay(data: WxJsapiSignResult) {
:deep(.van-key--blue) {
background: @color;
}
.wchatMpPay {
position: relative;
.container {
background: linear-gradient(to bottom, #07c160, #07c160, #ffffff); // 从蓝色渐变到白色
width: 100%;
padding: 40px;
height: 40%;
border-radius: 10px;
text-align: center;
color: white;
.container {
background: linear-gradient(to bottom, #07c160, #07c160, #ffffff); // 从蓝色渐变到白色
width: 100%;
padding: 40px;
height: 40%;
border-radius: 10px;
text-align: center;
color: white;
.payName {
margin: 5px 0;
font-size: 16px;
.payName {
margin: 5px 0;
font-size: 16px;
.paytext {
font-size: 20px;
margin-left: 5px;
font-weight: 600;
}
}
.amount-display {
background-color: white;
color: @color;
border-radius: 20px;
padding: 20px;
margin: 20px 0;
display: flex;
gap: 1.875rem;
align-items: center;
p {
font-size: 32px;
}
.title {
font-size: 20px;
}
.paytext {
font-size: 20px;
margin-left: 5px;
font-weight: 600;
}
}
.notes {
width: 100%;
height: 10%;
.amount-display {
background-color: white;
color: @color;
border-radius: 20px;
padding: 20px;
margin: 20px 0;
display: flex;
justify-content: center;
gap: 1.875rem;
align-items: center;
.remark {
color: @color;
cursor: pointer;
.remarkDecript {
max-width: 75vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
p {
font-size: 32px;
}
.title {
font-size: 20px;
}
}
.redirectPayBtn {
width: 100%;
margin: 0 auto;
position: absolute;
bottom: 3.125rem;
display: flex;
justify-content: center;
.van-button {
width: 90%;
}
}
.notes {
width: 100%;
height: 10%;
display: flex;
justify-content: center;
align-items: center;
.remark {
color: @color;
cursor: pointer;
}
}