diff --git a/src/app/pay/page.tsx b/src/app/pay/page.tsx
index 7a14098..39132e5 100644
--- a/src/app/pay/page.tsx
+++ b/src/app/pay/page.tsx
@@ -486,6 +486,7 @@ function PayContent() {
onBack={handleBack}
dark={isDark}
isEmbedded={isEmbedded}
+ isMobile={isMobile}
/>
)}
diff --git a/src/components/PaymentQRCode.tsx b/src/components/PaymentQRCode.tsx
index 4dea432..bfea193 100644
--- a/src/components/PaymentQRCode.tsx
+++ b/src/components/PaymentQRCode.tsx
@@ -18,6 +18,7 @@ interface PaymentQRCodeProps {
onBack: () => void;
dark?: boolean;
isEmbedded?: boolean;
+ isMobile?: boolean;
}
const TEXT_EXPIRED = '\u8BA2\u5355\u5DF2\u8D85\u65F6';
@@ -26,6 +27,7 @@ const TEXT_GO_PAY = '\u70B9\u51FB\u524D\u5F80\u652F\u4ED8';
const TEXT_SCAN_PAY = '\u8BF7\u4F7F\u7528\u652F\u4ED8\u5E94\u7528\u626B\u7801\u652F\u4ED8';
const TEXT_BACK = '\u8FD4\u56DE';
const TEXT_CANCEL_ORDER = '\u53D6\u6D88\u8BA2\u5355';
+const TEXT_H5_HINT = '\u652F\u4ED8\u5B8C\u6210\u540E\u8BF7\u8FD4\u56DE\u6B64\u9875\u9762\uFF0C\u7CFB\u7EDF\u5C06\u81EA\u52A8\u786E\u8BA4';
const TERMINAL_STATUSES = new Set(['COMPLETED', 'FAILED', 'CANCELLED', 'EXPIRED', 'REFUNDED', 'REFUND_FAILED']);
export default function PaymentQRCode({
@@ -43,6 +45,7 @@ export default function PaymentQRCode({
onBack,
dark = false,
isEmbedded = false,
+ isMobile = false,
}: PaymentQRCodeProps) {
const displayAmount = payAmountProp ?? amount;
const hasFeeDiff = payAmountProp !== undefined && payAmountProp !== amount;
@@ -416,6 +419,21 @@ export default function PaymentQRCode({
>
)}
+ ) : isMobile && payUrl ? (
+ <>
+
+
+ {`打开${channelLabel}支付`}
+
+
+ {TEXT_H5_HINT} +
+ > ) : ( <> {qrDataUrl && ( diff --git a/src/lib/pay-utils.ts b/src/lib/pay-utils.ts index 00896e9..c019fe3 100644 --- a/src/lib/pay-utils.ts +++ b/src/lib/pay-utils.ts @@ -38,12 +38,19 @@ export const FILTER_OPTIONS: { key: OrderStatusFilter; label: string }[] = [ export function detectDeviceIsMobile(): boolean { if (typeof window === 'undefined') return false; + // 1. 现代 API(Chromium 系浏览器,最准确) + const uad = (navigator as Navigator & { userAgentData?: { mobile: boolean } }).userAgentData; + if (uad !== undefined) return uad.mobile; + + // 2. UA 正则兜底(Safari / Firefox 等) const ua = navigator.userAgent || ''; const mobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Windows Phone|Mobile/i.test(ua); + if (mobileUA) return true; + + // 3. 触控 + 小屏兜底(新版 iPad UA 伪装成 Mac 的情况) const smallPhysicalScreen = Math.min(window.screen.width, window.screen.height) <= 768; const touchCapable = navigator.maxTouchPoints > 1; - - return mobileUA || (touchCapable && smallPhysicalScreen); + return touchCapable && smallPhysicalScreen; } export function formatStatus(status: string): string {