fix(weixin-sdk): fix iOS scanQRCode false camera permission error
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,57 @@
|
||||
import { getRuntimePlatform } from './env';
|
||||
import { getRuntimePlatform, isIosDevice, isWechatDevTools } from './env';
|
||||
import { ensureJssdkReady } from './jssdk';
|
||||
import type { WeixinSdkConfig } from './types';
|
||||
|
||||
function delay(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => window.setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/** 将微信 scanQRCode fail 的 errMsg 转为用户可读文案(避免误判为系统相机权限) */
|
||||
export function formatScanFailMessage(errMsg: string): string {
|
||||
const msg = errMsg.trim() || '扫码失败';
|
||||
if (/cancel/i.test(msg)) return '';
|
||||
|
||||
// iOS 常见:JSSDK 权限离线校验尚未完成,并非系统相机权限
|
||||
if (/offline verifying|permission value is offline/i.test(msg)) {
|
||||
return '微信权限验证中,请稍候再试或刷新页面后重新扫码';
|
||||
}
|
||||
|
||||
if (/invalid signature|config:fail|signature/i.test(msg)) {
|
||||
return '微信 JSSDK 签名校验失败,请刷新页面后重试';
|
||||
}
|
||||
|
||||
// 系统级相机权限(iPhone 设置 → 隐私 → 相机 → 微信)
|
||||
if (/system.*camera|NotAllowedError|camera.*not.*allowed|无法访问相机|无相机权限/i.test(msg)) {
|
||||
return '相机权限未开启,请在 iPhone「设置 → 隐私与安全性 → 相机」中允许微信使用摄像头';
|
||||
}
|
||||
|
||||
// JSSDK 接口授权失败(含 permission,但不等于相机权限)
|
||||
if (/permission|auth|denied|授权|拒绝/i.test(msg)) {
|
||||
return `微信扫码接口未就绪(${msg}),请刷新页面后重试`;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
function invokeScanQrCode(): Promise<string | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.wx!.scanQRCode!({
|
||||
needResult: 1,
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => resolve(res.resultStr || null),
|
||||
fail: (res) => {
|
||||
const msg = res.errMsg || '扫码失败';
|
||||
if (/cancel/i.test(msg)) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
const formatted = formatScanFailMessage(msg);
|
||||
reject(new Error(formatted || msg));
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 调起扫码(返回二维码/条码内容,失败返回 null) */
|
||||
export async function scanQrCode(config: WeixinSdkConfig): Promise<string | null> {
|
||||
const platform = getRuntimePlatform();
|
||||
@@ -26,25 +76,29 @@ export async function scanQrCode(config: WeixinSdkConfig): Promise<string | null
|
||||
if (!window.wx?.scanQRCode) {
|
||||
throw new Error('当前微信版本不支持扫码,请升级微信后重试');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
window.wx!.scanQRCode!({
|
||||
needResult: 1,
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => resolve(res.resultStr || null),
|
||||
fail: (res) => {
|
||||
const msg = res.errMsg || '扫码失败';
|
||||
if (/cancel/i.test(msg)) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
if (/permission|auth|denied|授权|拒绝|camera/i.test(msg)) {
|
||||
reject(new Error('相机权限未开启,请在微信设置中允许使用摄像头'));
|
||||
return;
|
||||
}
|
||||
reject(new Error(msg));
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// iOS 在 wx.ready 后立即调 scanQRCode 可能触发 offline verifying
|
||||
if (isIosDevice() && !isWechatDevTools()) {
|
||||
await delay(500);
|
||||
}
|
||||
|
||||
const maxAttempts = 2;
|
||||
let lastError: Error | null = null;
|
||||
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
||||
if (attempt > 0) {
|
||||
await delay(800);
|
||||
}
|
||||
try {
|
||||
return await invokeScanQrCode();
|
||||
} catch (e) {
|
||||
lastError = e instanceof Error ? e : new Error('扫码失败');
|
||||
const raw = lastError.message;
|
||||
const retryable = /offline verifying|权限验证中|接口未就绪/i.test(raw);
|
||||
if (attempt < maxAttempts - 1 && retryable) continue;
|
||||
throw lastError;
|
||||
}
|
||||
}
|
||||
throw lastError ?? new Error('扫码失败');
|
||||
}
|
||||
|
||||
throw new Error('请在微信内打开以使用扫码核销');
|
||||
|
||||
Reference in New Issue
Block a user