mirror of
https://gitee.com/bootx/dax-pay-h5
synced 2026-08-11 15:15:34 +08:00
feat(aggregate): 支付宝客户端内扫码 URL 直跳 + 支付完成关闭 webview
- 支付宝客户端内 precreate 返回的 qrCode 为可拉起 URL, 直接 location.href 跳转, 不再渲染二维码 - 支付中 redirect/form 结果置 redirecting 标志, 跳转前不重置 paying 避免闪现订单卡 - 新增 closeOnPaidWithoutReturn, 支付成功无商户 returnUrl 时关闭 webview 回到宿主钱包
This commit is contained in:
34
src/shared/pay/close-webview.ts
Normal file
34
src/shared/pay/close-webview.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 关闭当前 webview,回到宿主钱包
|
||||
*
|
||||
* 适用场景:聚合支付完成后无商户 returnUrl 时,关闭页面回到支付宝/微信钱包。
|
||||
* - 支付宝:AlipayJSBridge.call('closeWebview')
|
||||
* - 微信:WeixinJSBridge.call('closeWindow')
|
||||
* - 云闪付/抖音/浏览器:window.close() 兜底(多数 webview 不生效,但无副作用)
|
||||
*
|
||||
* 与 [jsapi.ts] 的桥判断模式对称:宿主桥未注入时静默降级,不抛错
|
||||
* (关闭是 fire-and-forget,不能因为关不掉页面而报错影响已完成的支付体验)。
|
||||
*/
|
||||
export function closeWebview(): void {
|
||||
if (typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
const w = window as any
|
||||
|
||||
// 支付宝:优先用 AlipayJSBridge
|
||||
if (w.AlipayJSBridge?.call) {
|
||||
w.AlipayJSBridge.call('closeWebview')
|
||||
return
|
||||
}
|
||||
|
||||
// 微信:用 WeixinJSBridge
|
||||
if (w.WeixinJSBridge?.call) {
|
||||
w.WeixinJSBridge.call('closeWindow')
|
||||
return
|
||||
}
|
||||
|
||||
// 兜底:云闪付/抖音/浏览器(多数不生效,无副作用)
|
||||
if (typeof w.close === 'function') {
|
||||
w.close()
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getGatewayOrder,
|
||||
} from '@/shared/api/gateway'
|
||||
import { useGatewayOrderPoll } from '@/shared/hooks/use-gateway-order-poll'
|
||||
import { closeWebview } from '@/shared/pay/close-webview'
|
||||
import { invokeJsapiByEnv } from '@/shared/pay/jsapi'
|
||||
import { buildAggregateEnvPath } from '@/shared/utils/client-env'
|
||||
import { fenToYuan } from '@/shared/utils/pay-amount'
|
||||
@@ -31,6 +32,12 @@ export interface UseAggregatePayOptions {
|
||||
onError?: (message: string) => void
|
||||
/** i18n 文案回调 */
|
||||
t: (key: string) => string
|
||||
/**
|
||||
* 支付成功且无商户 returnUrl 时,自动关闭 webview 回到宿主钱包
|
||||
* 聚合扫码场景建议开启(用户没有业务回跳地址,付完应回到支付宝/微信钱包)
|
||||
* 与 returnUrl 跳转互斥:有 returnUrl 走跳转,无 returnUrl 且开关开启才关闭
|
||||
*/
|
||||
closeOnPaidWithoutReturn?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,11 +61,14 @@ export function useAggregatePay(options: UseAggregatePayOptions) {
|
||||
onPaid,
|
||||
onError,
|
||||
t,
|
||||
closeOnPaidWithoutReturn = false,
|
||||
} = options
|
||||
|
||||
const loading = ref(true)
|
||||
const paying = ref(false)
|
||||
const authorizing = ref(false)
|
||||
// 跳转类支付结果标志: location.href 触发后页面卸载前 finally 不重置 paying, 避免闪现订单卡
|
||||
let redirecting = false
|
||||
const loadError = ref('')
|
||||
const order = ref<GatewayOrderInfo>({})
|
||||
const meta = ref<AggregatePayMeta>({})
|
||||
@@ -96,9 +106,18 @@ export function useAggregatePay(options: UseAggregatePayOptions) {
|
||||
|
||||
function redirectIfNeeded() {
|
||||
if (order.value.returnUrl) {
|
||||
// 有商户回跳地址:延迟跳转,让 toast 先显示
|
||||
setTimeout(() => {
|
||||
window.location.href = order.value.returnUrl!
|
||||
}, 1200)
|
||||
return
|
||||
}
|
||||
// 无 returnUrl 且开启自动关闭:延迟关闭 webview 回到宿主钱包
|
||||
// 比 returnUrl 跳转稍晚,让成功 toast 显示完
|
||||
if (closeOnPaidWithoutReturn) {
|
||||
setTimeout(() => {
|
||||
closeWebview()
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,14 +195,25 @@ export function useAggregatePay(options: UseAggregatePayOptions) {
|
||||
redirectIfNeeded()
|
||||
break
|
||||
case 'redirect':
|
||||
// 跳转类结果: 保持 paying, 避免 finally 重置导致跳转前闪现订单卡
|
||||
redirecting = true
|
||||
redirectToPayUrl(action.url)
|
||||
break
|
||||
case 'qrcode':
|
||||
// 支付宝客户端内: precreate 返回的 qrCode 是可拉起的 URL, 直接跳转而非显示二维码
|
||||
if (clientEnv === 'alipay' && /^https?:\/\//i.test(action.content)) {
|
||||
redirecting = true
|
||||
redirectToPayUrl(action.content)
|
||||
startPoll(orderNo)
|
||||
break
|
||||
}
|
||||
qrContent.value = action.content
|
||||
showQrcode.value = true
|
||||
startPoll(orderNo)
|
||||
break
|
||||
case 'form':
|
||||
// 表单提交会导航离开本页, 同属跳转类
|
||||
redirecting = true
|
||||
submitPayForm(action.html)
|
||||
startPoll(orderNo)
|
||||
break
|
||||
@@ -220,6 +250,7 @@ export function useAggregatePay(options: UseAggregatePayOptions) {
|
||||
return
|
||||
}
|
||||
paying.value = true
|
||||
redirecting = false
|
||||
showQrcode.value = false
|
||||
qrContent.value = ''
|
||||
try {
|
||||
@@ -236,7 +267,10 @@ export function useAggregatePay(options: UseAggregatePayOptions) {
|
||||
onError?.(e?.message || t('aggregate.payFail'))
|
||||
}
|
||||
finally {
|
||||
paying.value = false
|
||||
// 跳转类结果保持 paying=true, 让 loading 稳定显示到页面卸载
|
||||
if (!redirecting) {
|
||||
paying.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user