feat(h5/cashier): 成功态升级为完整结果卡片并接入 returnUrl 倒计时跳转

This commit is contained in:
bootx
2026-07-20 23:17:23 +08:00
parent f94148e4a8
commit f83b57c153
12 changed files with 291 additions and 70 deletions

View File

@@ -4,8 +4,8 @@
* 负责订单展示、支付项选择、发起支付与 payBody 分发
*/
import type { CashierItemPublic, GatewayOrderInfo } from '@/shared/api/gateway'
import { showNotify, showSuccessToast } from 'vant'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { showNotify } from 'vant'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import {
@@ -21,6 +21,7 @@ import { useGatewayOrderPoll } from '@/shared/hooks/use-gateway-order-poll'
import { closeWebview } from '@/shared/pay/close-webview'
import { useGatewayAuth } from '@/shared/pay/use-gateway-auth'
import { detectClientEnv, isValidH5ClientEnv } from '@/shared/utils/client-env'
import { formatDateTime } from '@/shared/utils/datetime'
import { cacheOrder, clearCachedOrder, getCachedOrder } from '@/shared/utils/order-cache'
import { fenToYuan } from '@/shared/utils/pay-amount'
import { invokeJsapiByEnv } from '@/shared/utils/pay-jsapi'
@@ -72,13 +73,17 @@ const isTerminal = computed(() =>
paid.value || closed.value || failed.value || order.value.status === 'expired' || expired.value,
)
// 结果态类型(关单/失败/过期/加载失败),非空时渲染结果卡片
type ResultState = 'closed' | 'failed' | 'expired' | 'loadError'
// 结果态类型(成功/关单/失败/过期/加载失败),非空时渲染结果卡片
type ResultState = 'paid' | 'closed' | 'failed' | 'expired' | 'loadError'
const resultState = computed<ResultState | ''>(() => {
// 订单加载失败优先(订单不存在/网络异常)
if (loadError.value) {
return 'loadError'
}
// 支付成功(优先于其他终态,展示完整成功卡片)
if (paid.value) {
return 'paid'
}
if (closed.value) {
return 'closed'
}
@@ -94,6 +99,9 @@ const resultState = computed<ResultState | ''>(() => {
// 结果态展示元数据(图标/标题/副文案/主题色)
const resultMeta = computed(() => {
switch (resultState.value) {
case 'paid':
// 支付成功: 微信绿(与码牌/聚合一致)
return { icon: 'success', titleKey: 'cashier.paid', tipKey: 'cashier.paidTip', color: '#07c160' }
case 'closed':
return { icon: 'lock', titleKey: 'cashier.closed', tipKey: 'cashier.closedTip', color: '#fa8c16' }
case 'failed':
@@ -118,9 +126,8 @@ const { startPoll, stopPoll } = useGatewayOrderPoll({
order.value = latest
},
onPaid(latest) {
// 轮询检测到已支付: 仅更新订单,成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
order.value = latest
showSuccessToast(t('cashier.paid'))
redirectIfNeeded()
},
})
@@ -148,16 +155,59 @@ function selectPayMethod(itemId: string) {
}
/**
* 支付成功后跳转商户 returnUrl
* 支付成功后跳转商户 returnUrl - 倒计时方案
*
* 进入 paid 终态后,若有 returnUrl,展示 3 秒倒计时,归零自动 location.href;
* 用户也可点「返回商户」按钮立即跳转。替代原 1.2s setTimeout 闪跳,
* 让用户能看清成功卡片再跳。
*/
function redirectIfNeeded() {
if (order.value.returnUrl) {
setTimeout(() => {
window.location.href = order.value.returnUrl!
}, 1200)
const REDIRECT_COUNTDOWN_SECONDS = 3
const redirectCountdown = ref(0)
let redirectTimer: ReturnType<typeof setInterval> | null = null
function clearRedirectTimer() {
if (redirectTimer) {
clearInterval(redirectTimer)
redirectTimer = null
}
}
/**
* 启动可视化倒计时,由 watch(resultState) 在进入 paid 终态时触发
*/
function startRedirectCountdown() {
if (!order.value.returnUrl || redirectTimer) {
return
}
redirectCountdown.value = REDIRECT_COUNTDOWN_SECONDS
redirectTimer = setInterval(() => {
redirectCountdown.value--
if (redirectCountdown.value <= 0) {
clearRedirectTimer()
if (order.value.returnUrl) {
window.location.href = order.value.returnUrl
}
}
}, 1000)
}
/**
* 用户点击「返回商户」立即跳转(不等倒计时)
*/
function redirectNow() {
clearRedirectTimer()
if (order.value.returnUrl) {
window.location.href = order.value.returnUrl
}
}
// 进入 paid 终态时启动倒计时跳转(由 status 变化驱动,所有触发 paid 的路径统一在此处理)
watch(resultState, (state) => {
if (state === 'paid') {
startRedirectCountdown()
}
})
/**
* 启动过期倒计时
*/
@@ -324,9 +374,8 @@ async function ensureOpenIdOrRedirect(): Promise<string | null> {
async function handleJsapi(payload: string) {
const status = await invokeJsapiByEnv(clientEnvParam, payload)
if (status === 'ok') {
// JSAPI 调起支付成功: 仅更新状态,成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
order.value.status = 'paid'
showSuccessToast(t('cashier.paid'))
redirectIfNeeded()
return
}
if (status === 'cancel') {
@@ -375,10 +424,9 @@ async function pay() {
switch (action.type) {
case 'success':
order.value.status = 'paid'
// 支付成功后订单状态已变清除缓存避免回显未付态
// 支付成功后订单状态已变,清除缓存避免回显未付态
clearCachedOrder(orderNo)
showSuccessToast(t('cashier.paid'))
redirectIfNeeded()
// 成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
break
case 'redirect':
redirectToPayUrl(action.url)
@@ -421,6 +469,7 @@ onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
}
clearRedirectTimer()
stopPoll()
})
</script>
@@ -433,13 +482,51 @@ onUnmounted(() => {
:tip-key="paying ? 'cashier.paying' : ''"
/>
<!-- 结果态订单已关闭/支付失败/已过期/加载失败 -->
<!-- 结果态: 支付成功/订单已关闭/支付失败/已过期/加载失败 -->
<div v-else-if="resultState" class="cashier__result">
<!-- 成功态: 实心彩色圆底 + 白色对勾(对齐 AggregateResultCard);其他态: 半透明底 + 彩色 SVG -->
<div
class="cashier__result-icon"
:style="{ color: resultMeta.color, background: `${resultMeta.color}1a` }"
:style="{
color: resultMeta.color,
background: resultState === 'paid' ? resultMeta.color : `${resultMeta.color}1a`,
}"
>
<van-icon :name="resultMeta.icon" size="56" />
<svg
viewBox="0 0 48 48"
width="40"
height="40"
fill="none"
:stroke="resultState === 'paid' ? '#fff' : resultMeta.color"
:stroke-width="resultState === 'paid' ? 4 : 3.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<!-- 成功: 对勾 -->
<template v-if="resultState === 'paid'">
<path d="M14 24 L21 31 L34 16" />
</template>
<!-- 订单已关闭: -->
<template v-else-if="resultState === 'closed'">
<rect x="10" y="22" width="28" height="18" rx="3" />
<path d="M15 22 V15 a9 9 0 0 1 18 0 V22" />
</template>
<!-- 支付失败: 叉号 -->
<template v-else-if="resultState === 'failed'">
<path d="M14 14 L34 34 M34 14 L14 34" stroke-width="4" />
</template>
<!-- 已过期: 时钟 -->
<template v-else-if="resultState === 'expired'">
<circle cx="24" cy="24" r="17" />
<path d="M24 13 V24 L32 28" />
</template>
<!-- 加载失败: 警告三角 -->
<template v-else>
<path d="M24 6 L43 39 H5 Z" />
<path d="M24 19 V29" />
<circle cx="24" cy="35" r="1.5" :fill="resultMeta.color" stroke="none" />
</template>
</svg>
</div>
<div class="cashier__result-title">
{{ t(resultMeta.titleKey) }}
@@ -461,8 +548,23 @@ onUnmounted(() => {
<span>{{ t('cashier.payableAmount') }}</span>
<span class="cashier__result-amount">{{ amountYuan }}</span>
</div>
<!-- 成功态额外展示支付时间 -->
<div v-if="resultState === 'paid' && order.payTime" class="cashier__result-order-row">
<span>{{ t('cashier.payTime') }}</span>
<span :title="order.payTime">{{ formatDateTime(order.payTime) }}</span>
</div>
</div>
<button class="cashier__result-btn" @click="closeWebview">
<!-- 成功态且有 returnUrl: 倒计时提示 + 返回商户按钮 -->
<template v-if="resultState === 'paid' && order.returnUrl">
<p v-if="redirectCountdown > 0" class="cashier__result-countdown">
{{ t('cashier.autoRedirectTip', { n: redirectCountdown }) }}
</p>
<button class="cashier__result-btn" @click="redirectNow">
{{ t('cashier.backToMerchant') }}
</button>
</template>
<!-- 其他情况: 关闭页面按钮 -->
<button v-else class="cashier__result-btn" @click="closeWebview">
{{ t('cashier.closePage') }}
</button>
</div>
@@ -496,15 +598,8 @@ onUnmounted(() => {
</div>
</div>
<!-- 已支付 -->
<div v-if="paid" class="cashier__body enter-y">
<div class="cashier__section-title">
{{ t('cashier.paid') }}
</div>
</div>
<!-- 二维码支付 -->
<div v-else-if="showQrcode" class="cashier__body enter-y">
<div v-if="showQrcode" class="cashier__body enter-y">
<div class="cashier__section-title">
{{ t('cashier.qrcodePay') }}
</div>
@@ -548,8 +643,8 @@ onUnmounted(() => {
</div>
</div>
<!-- 底部支付按钮 -->
<div v-if="!paid && !showQrcode" class="cashier__footer enter-y">
<!-- 底部支付按钮(paid 已由 resultState 卡片接管,此处不会进入 paid ) -->
<div v-if="!showQrcode" class="cashier__footer enter-y">
<button
class="cashier__pay-btn"
:disabled="paying || !selectId || expired"
@@ -675,6 +770,15 @@ onUnmounted(() => {
font-weight: 600;
}
// 自动跳转倒计时提示(仅成功态 + 有 returnUrl)
&__result-countdown {
margin: 16px 0 0;
font-size: 12px;
color: var(--h5-text-secondary);
line-height: 1.6;
font-variant-numeric: tabular-nums;
}
&__result-btn {
// 流式底栏,不再 fixed避免被内容遮挡
margin-top: 32px;

View File

@@ -3,7 +3,7 @@
* PC WEB 收银台cashierType=web不按 clientEnv 分桶)
*/
import type { CashierItemPublic, GatewayOrderInfo } from '@/shared/api/gateway'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
import { cashierPay, getGatewayOrder, listCashierItems } from '@/shared/api/gateway'
@@ -12,6 +12,7 @@ import PayMethodIcon from '@/shared/components/pay/PayMethodIcon.vue'
import QrCodeDisplay from '@/shared/components/pay/QrCodeDisplay.vue'
import { useGatewayOrderPoll } from '@/shared/hooks/use-gateway-order-poll'
import { closeWebview } from '@/shared/pay/close-webview'
import { formatDateTime } from '@/shared/utils/datetime'
import { fenToYuan } from '@/shared/utils/pay-amount'
import {
redirectToPayUrl,
@@ -52,13 +53,17 @@ const isTerminal = computed(() =>
paid.value || closed.value || failed.value || order.value.status === 'expired' || expired.value,
)
// 结果态类型(关单/失败/过期/加载失败),非空时渲染结果卡片
type ResultState = 'closed' | 'failed' | 'expired' | 'loadError'
// 结果态类型(成功/关单/失败/过期/加载失败),非空时渲染结果卡片
type ResultState = 'paid' | 'closed' | 'failed' | 'expired' | 'loadError'
const resultState = computed<ResultState | ''>(() => {
// 订单加载失败优先(订单不存在/网络异常)
if (loadError.value) {
return 'loadError'
}
// 支付成功(优先于其他终态,展示完整成功卡片)
if (paid.value) {
return 'paid'
}
if (closed.value) {
return 'closed'
}
@@ -74,14 +79,17 @@ const resultState = computed<ResultState | ''>(() => {
// 结果态展示元数据(图标/标题/副文案/主题色)
const resultMeta = computed(() => {
switch (resultState.value) {
case 'paid':
// 支付成功: 微信绿(与 H5/码牌/聚合一致)
return { icon: 'check', titleKey: 'cashier.paid', tipKey: 'cashier.paidTip', color: '#07c160' }
case 'closed':
return { icon: 'lock', titleKey: 'cashier.closed', tipKey: 'cashier.closedTip', color: '#fa8c16' }
case 'failed':
return { icon: 'cross', titleKey: 'cashier.failed', tipKey: 'cashier.failedTip', color: '#ff4d4f' }
case 'expired':
return { icon: 'clock-o', titleKey: 'cashier.expired', tipKey: 'cashier.expiredTip', color: '#8c8c8c' }
return { icon: 'clock', titleKey: 'cashier.expired', tipKey: 'cashier.expiredTip', color: '#8c8c8c' }
default:
return { icon: 'warning-o', titleKey: 'cashier.loadFail', tipKey: 'cashier.loadFailTip', color: '#fa8c16' }
return { icon: 'warning', titleKey: 'cashier.loadFail', tipKey: 'cashier.loadFailTip', color: '#fa8c16' }
}
})
@@ -98,13 +106,62 @@ const { startPoll, stopPoll } = useGatewayOrderPoll({
order.value = latest
},
onPaid(latest) {
// 轮询检测到已支付: 仅更新订单,成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
order.value = latest
if (latest.returnUrl) {
window.location.href = latest.returnUrl
}
},
})
/**
* 支付成功后跳转商户 returnUrl - 倒计时方案
*
* 进入 paid 终态后,若有 returnUrl,展示 3 秒倒计时,归零自动 location.href;
* 用户也可点「返回商户」按钮立即跳转。替代原 onPaid 内的立即跳转,
* 让用户能看清成功卡片再跳。
*/
const REDIRECT_COUNTDOWN_SECONDS = 3
const redirectCountdown = ref(0)
let redirectTimer: ReturnType<typeof setInterval> | null = null
function clearRedirectTimer() {
if (redirectTimer) {
clearInterval(redirectTimer)
redirectTimer = null
}
}
function startRedirectCountdown() {
if (!order.value.returnUrl || redirectTimer) {
return
}
redirectCountdown.value = REDIRECT_COUNTDOWN_SECONDS
redirectTimer = setInterval(() => {
redirectCountdown.value--
if (redirectCountdown.value <= 0) {
clearRedirectTimer()
if (order.value.returnUrl) {
window.location.href = order.value.returnUrl
}
}
}, 1000)
}
/**
* 用户点击「返回商户」立即跳转(不等倒计时)
*/
function redirectNow() {
clearRedirectTimer()
if (order.value.returnUrl) {
window.location.href = order.value.returnUrl
}
}
// 进入 paid 终态时启动倒计时跳转(由 status 变化驱动,所有触发 paid 的路径统一在此处理)
watch(resultState, (state) => {
if (state === 'paid') {
startRedirectCountdown()
}
})
function methodName(item?: CashierItemPublic | null) {
if (!item) {
return ''
@@ -200,9 +257,7 @@ async function pay() {
switch (action.type) {
case 'success':
order.value.status = 'paid'
if (order.value.returnUrl) {
window.location.href = order.value.returnUrl
}
// 成功卡片由 resultState 渲染、倒计时跳转由 watch 触发
break
case 'redirect':
redirectToPayUrl(action.url)
@@ -253,6 +308,7 @@ onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
}
clearRedirectTimer()
stopPoll()
})
</script>
@@ -262,11 +318,15 @@ onUnmounted(() => {
<div class="pc-cashier__box">
<!-- 加载态统一全屏遮罩替代原卡片内 spinner -->
<InitLoadingMask v-if="loading" />
<!-- 结果态订单已关闭/支付失败/已过期/加载失败 -->
<!-- 结果态: 支付成功/订单已关闭/支付失败/已过期/加载失败 -->
<div v-else-if="resultState" class="pc-cashier__result">
<!-- 成功态: 实心彩色圆底 + 白色对勾(对齐 H5/AggregateResultCard);其他态: 半透明底 + 彩色 SVG -->
<div
class="pc-cashier__result-icon"
:style="{ color: resultMeta.color, background: `${resultMeta.color}1a` }"
:style="{
color: resultMeta.color,
background: resultState === 'paid' ? resultMeta.color : `${resultMeta.color}1a`,
}"
>
<svg
class="pc-cashier__result-svg"
@@ -274,13 +334,17 @@ onUnmounted(() => {
width="56"
height="56"
fill="none"
:stroke="resultMeta.color"
stroke-width="3.5"
:stroke="resultState === 'paid' ? '#fff' : resultMeta.color"
:stroke-width="resultState === 'paid' ? 4 : 3.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<!-- 支付成功: 对勾 -->
<template v-if="resultState === 'paid'">
<path d="M14 24 L21 31 L34 16" />
</template>
<!-- 支付失败: 叉号 -->
<template v-if="resultState === 'failed'">
<template v-else-if="resultState === 'failed'">
<path d="M14 14 L34 34 M34 14 L14 34" stroke-width="4" />
</template>
<!-- 已过期: 时钟 -->
@@ -321,8 +385,23 @@ onUnmounted(() => {
<span>{{ t('cashier.payableAmount') }}</span>
<span class="pc-cashier__result-amount">{{ amountYuan }}</span>
</div>
<!-- 成功态额外展示支付时间 -->
<div v-if="resultState === 'paid' && order.payTime" class="pc-cashier__result-row">
<span>{{ t('cashier.payTime') }}</span>
<span :title="order.payTime">{{ formatDateTime(order.payTime) }}</span>
</div>
</div>
<button class="pc-cashier__result-btn" @click="closePage">
<!-- 成功态且有 returnUrl: 倒计时提示 + 返回商户按钮 -->
<template v-if="resultState === 'paid' && order.returnUrl">
<p v-if="redirectCountdown > 0" class="pc-cashier__result-countdown">
{{ t('cashier.autoRedirectTip', { n: redirectCountdown }) }}
</p>
<button class="pc-cashier__result-btn" @click="redirectNow">
{{ t('cashier.backToMerchant') }}
</button>
</template>
<!-- 其他情况: 关闭页面按钮 -->
<button v-else class="pc-cashier__result-btn" @click="closePage">
{{ t('cashier.closePage') }}
</button>
</div>
@@ -357,10 +436,7 @@ onUnmounted(() => {
<!-- 内容区 -->
<div class="pc-cashier__content">
<div v-if="paid" class="pc-cashier__paid">
{{ t('cashier.paid') }}
</div>
<div v-else-if="payError" class="pc-cashier__error">
<div v-if="payError" class="pc-cashier__error">
{{ payError }}
</div>
<!-- 支付方式选择 -->
@@ -574,14 +650,6 @@ onUnmounted(() => {
margin-bottom: 16px;
}
.pc-cashier__paid {
font-size: 18px;
font-weight: 600;
color: #07c160;
text-align: center;
padding: 40px 0;
}
.pc-cashier__error {
color: #ff4d4f;
font-size: 14px;
@@ -672,6 +740,15 @@ onUnmounted(() => {
font-weight: 600;
}
/* 自动跳转倒计时提示(仅成功态 + 有 returnUrl) */
.pc-cashier__result-countdown {
margin: 20px 0 0;
font-size: 13px;
color: var(--h5-text-secondary);
line-height: 1.6;
font-variant-numeric: tabular-nums;
}
.pc-cashier__result-btn {
margin-top: 32px;
width: 240px;

View File

@@ -37,5 +37,9 @@
"failedTip": "Payment was unsuccessful. Please try again.",
"expiredTip": "The order was not paid in time and has expired.",
"loadFailTip": "Please check that the order link is correct.",
"closePage": "Close"
"closePage": "Close",
"paidTip": "Payment completed. Thank you",
"autoRedirectTip": "Auto-return to merchant in {n}s",
"backToMerchant": "Return to merchant",
"payTime": "Payment time"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "Pembayaran tidak berhasil. Silakan coba lagi.",
"expiredTip": "Pesanan tidak dibayar tepat waktu dan telah kedaluwarsa.",
"loadFailTip": "Pastikan tautan pesanan sudah benar.",
"closePage": "Tutup"
"closePage": "Tutup",
"paidTip": "Pembayaran selesai. Terima kasih",
"autoRedirectTip": "Kembali ke merchant otomatis dalam {n} detik",
"backToMerchant": "Kembali ke merchant",
"payTime": "Waktu pembayaran"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "支払いが完了しませんでした。もう一度お試しください",
"expiredTip": "支払い期限を過ぎたため注文は期限切れとなりました",
"loadFailTip": "注文リンクが正しいかご確認ください",
"closePage": "ページを閉じる"
"closePage": "ページを閉じる",
"paidTip": "支払いが完了しました。ありがとうございます",
"autoRedirectTip": "{n} 秒後に自動的に店舗に戻ります",
"backToMerchant": "店舗に戻る",
"payTime": "支払い日時"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "결제가 완료되지 않았습니다. 다시 시도해 주세요",
"expiredTip": "시간 내에 결제하지 않아 주문이 만료되었습니다",
"loadFailTip": "주문 링크가 올바른지 확인해 주세요",
"closePage": "페이지 닫기"
"closePage": "페이지 닫기",
"paidTip": "결제가 완료되었습니다. 감사합니다",
"autoRedirectTip": "{n}초 후 자동으로 가맹점으로 돌아갑니다",
"backToMerchant": "가맹점으로 돌아가기",
"payTime": "결제 시간"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "Pembayaran tidak berjaya. Sila cuba lagi.",
"expiredTip": "Pesanan tidak dibayar tepat masa dan telah tamat tempoh.",
"loadFailTip": "Sila pastikan pautan pesanan adalah betul.",
"closePage": "Tutup"
"closePage": "Tutup",
"paidTip": "Pembayaran selesai. Terima kasih",
"autoRedirectTip": "Kembali ke merchant automatik dalam {n} saat",
"backToMerchant": "Kembali ke merchant",
"payTime": "Masa pembayaran"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "การชำระเงินไม่สำเร็จ กรุณาลองอีกครั้ง",
"expiredTip": "ชำระเงินไม่ทันเวลา คำสั่งซื้อหมดอายุแล้ว",
"loadFailTip": "กรุณาตรวจสอบว่าลิงก์คำสั่งซื้อถูกต้อง",
"closePage": "ปิด"
"closePage": "ปิด",
"paidTip": "ชำระเงินสำเร็จ ขอบคุณที่ใช้บริการ",
"autoRedirectTip": "กลับสู่ร้านค้าอัตโนมัติใน {n} วินาที",
"backToMerchant": "กลับสู่ร้านค้า",
"payTime": "เวลาที่ชำระ"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "Thanh toán không thành công. Vui lòng thử lại.",
"expiredTip": "Đơn hàng quá hạn thanh toán và đã hết hạn.",
"loadFailTip": "Vui lòng kiểm tra lại đường dẫn đơn hàng.",
"closePage": "Đóng"
"closePage": "Đóng",
"paidTip": "Thanh toán hoàn tất. Cảm ơn bạn",
"autoRedirectTip": "Tự động quay lại merchant sau {n} giây",
"backToMerchant": "Quay lại merchant",
"payTime": "Thời gian thanh toán"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "支付未成功,请重新发起",
"expiredTip": "订单超时未支付,已自动关闭",
"loadFailTip": "请确认订单链接是否正确",
"closePage": "关闭页面"
"closePage": "关闭页面",
"paidTip": "支付已完成,感谢使用",
"autoRedirectTip": "{n} 秒后自动返回商户",
"backToMerchant": "返回商户",
"payTime": "支付时间"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "支付未成功,請重新發起",
"expiredTip": "訂單逾時未支付,已自動關閉",
"loadFailTip": "請確認訂單連結是否正確",
"closePage": "關閉頁面"
"closePage": "關閉頁面",
"paidTip": "支付已完成,感謝使用",
"autoRedirectTip": "{n} 秒後自動返回商戶",
"backToMerchant": "返回商戶",
"payTime": "支付時間"
}

View File

@@ -37,5 +37,9 @@
"failedTip": "支付未成功,請重新發起",
"expiredTip": "訂單逾時未支付,已自動關閉",
"loadFailTip": "請確認訂單連結是否正確",
"closePage": "關閉頁面"
"closePage": "關閉頁面",
"paidTip": "支付已完成,感謝使用",
"autoRedirectTip": "{n} 秒後自動返回商戶",
"backToMerchant": "返回商戶",
"payTime": "支付時間"
}