移动端浏览器支付 ok

This commit is contained in:
yml2213
2026-06-13 19:17:02 +08:00
parent 9d7184ecde
commit 6ce031160b
4 changed files with 337 additions and 39 deletions
@@ -0,0 +1,150 @@
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import QRCode from 'qrcode'
import { showDialog, showToast } from 'vant'
import { queryOrderPayment, type PaymentOrder } from '@/features/orders/api/orders'
type PaidHandler = () => Promise<void> | void
export function mobilePaymentPayURL(payment?: PaymentOrder | null) {
return payment?.jspay_url || payment?.td_code || payment?.jspay_info || ''
}
function isInAppPaymentBrowser() {
if (typeof navigator === 'undefined') return false
const ua = navigator.userAgent || ''
return /MicroMessenger|AlipayClient/i.test(ua)
}
function isHTTPURL(value: string) {
return /^https?:\/\//i.test(value)
}
export function useMobilePaymentCashier() {
const paymentPopupVisible = ref(false)
const activePayment = ref<PaymentOrder | null>(null)
const paymentQRCodeURL = ref('')
const qrGenerating = ref(false)
const checkingPayment = ref(false)
let paymentPollingTimer: number | undefined
let paidHandler: PaidHandler | undefined
const payURL = computed(() => mobilePaymentPayURL(activePayment.value))
async function openMobilePaymentCashier(payment: PaymentOrder, onPaid?: PaidHandler) {
activePayment.value = payment
paidHandler = onPaid
if (payment.paid) {
await handlePaid()
return
}
const currentPayURL = mobilePaymentPayURL(payment)
if (currentPayURL && isHTTPURL(currentPayURL) && isInAppPaymentBrowser()) {
window.location.href = currentPayURL
return
}
if (currentPayURL && isHTTPURL(currentPayURL)) {
paymentPopupVisible.value = true
await renderPaymentQRCode(payment)
startPaymentPolling()
return
}
await showDialog({
title: '订单支付',
message: currentPayURL || '支付单已创建,请稍后刷新订单状态。',
confirmButtonText: '知道了',
})
}
async function renderPaymentQRCode(payment = activePayment.value) {
const currentPayURL = mobilePaymentPayURL(payment)
paymentQRCodeURL.value = ''
if (!currentPayURL || !isHTTPURL(currentPayURL)) return
qrGenerating.value = true
try {
paymentQRCodeURL.value = await QRCode.toDataURL(currentPayURL, {
width: 220,
margin: 1,
errorCorrectionLevel: 'M',
color: {
dark: '#111827',
light: '#ffffff',
},
})
} catch {
showToast({ message: '二维码生成失败', icon: 'cross' })
} finally {
qrGenerating.value = false
}
}
function startPaymentPolling() {
stopPaymentPolling()
paymentPollingTimer = window.setInterval(() => {
void refreshPaymentStatus(true)
}, 2500)
}
function stopPaymentPolling() {
if (paymentPollingTimer === undefined) return
window.clearInterval(paymentPollingTimer)
paymentPollingTimer = undefined
}
async function refreshPaymentStatus(silent = false) {
if (!activePayment.value || checkingPayment.value) return
checkingPayment.value = true
const previousPayURL = payURL.value
try {
const payment = await queryOrderPayment(activePayment.value.order_id)
activePayment.value = payment
if (payment.paid) {
await handlePaid()
return
}
if (mobilePaymentPayURL(payment) !== previousPayURL) {
await renderPaymentQRCode(payment)
}
if (!silent) {
showToast({ message: '支付暂未完成', icon: 'info-o' })
}
} catch {
if (!silent) {
showToast({ message: '查询支付状态失败', icon: 'cross' })
}
} finally {
checkingPayment.value = false
}
}
async function handlePaid() {
stopPaymentPolling()
paymentPopupVisible.value = false
showToast({ message: '支付成功,等待号主交接', icon: 'passed' })
await paidHandler?.()
}
watch(paymentPopupVisible, visible => {
if (!visible) stopPaymentPolling()
})
onBeforeUnmount(stopPaymentPolling)
return {
paymentPopupVisible,
activePayment,
payURL,
paymentQRCodeURL,
qrGenerating,
checkingPayment,
openMobilePaymentCashier,
refreshPaymentStatus,
stopPaymentPolling,
}
}