商铺端核销,调用相机报无效签名修复
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
sessionFromWechatLogin,
|
||||
} from '../lib/wechat-auth';
|
||||
import { isWechatEnv, weixinSdk } from '../lib/weixin';
|
||||
import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk';
|
||||
import WechatScanAuthModal from '../components/WechatScanAuthModal';
|
||||
|
||||
const PENDING_SCAN_KEY = 'shop_pending_scan';
|
||||
@@ -20,6 +21,14 @@ function formatMoney(n: number) {
|
||||
return n.toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
function formatScanError(e: unknown): string {
|
||||
const msg = e instanceof Error ? e.message : '扫码失败,请重试';
|
||||
if (/invalid signature/i.test(msg)) {
|
||||
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 shop.runxian.top,并刷新页面后重试';
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
export default function HomePage() {
|
||||
const navigate = useNavigate();
|
||||
const { applySession } = useStoreSession();
|
||||
@@ -51,11 +60,12 @@ export default function HomePage() {
|
||||
}
|
||||
setAuthModalOpen(false);
|
||||
setAuthError('');
|
||||
stripOAuthParamsFromLocation();
|
||||
setSearchParams({}, { replace: true });
|
||||
const shouldScan = sessionStorage.getItem(PENDING_SCAN_KEY) === '1';
|
||||
sessionStorage.removeItem(PENDING_SCAN_KEY);
|
||||
if (shouldScan) {
|
||||
void runScan();
|
||||
window.setTimeout(() => void runScan(), 0);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -81,7 +91,7 @@ export default function HomePage() {
|
||||
}
|
||||
navigate(`/redeem?token=${encodeURIComponent(token)}`);
|
||||
} catch (e) {
|
||||
setScanMsg(e instanceof Error ? e.message : '扫码失败,请重试');
|
||||
setScanMsg(formatScanError(e));
|
||||
} finally {
|
||||
setScanning(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export { isWechatBrowser, isMiniProgram, getRuntimePlatform } from './env';
|
||||
export { initWechatJssdk, ensureJssdkReady, isJssdkReady } from './jssdk';
|
||||
export { initWechatJssdk, ensureJssdkReady, isJssdkReady, normalizeJssdkPageUrl, stripOAuthParamsFromLocation } from './jssdk';
|
||||
export {
|
||||
getWechatLocation,
|
||||
getWechatLocationDetailed,
|
||||
|
||||
@@ -8,7 +8,32 @@ let configured = false;
|
||||
let configuredUrl: string | null = null;
|
||||
|
||||
function currentPageUrl() {
|
||||
return typeof window !== 'undefined' ? window.location.href.split('#')[0] : '';
|
||||
return typeof window !== 'undefined' ? normalizeJssdkPageUrl(window.location.href) : '';
|
||||
}
|
||||
|
||||
/** 参与 JSSDK 签名的页面 URL(去掉 hash、OAuth 回跳参数) */
|
||||
export function normalizeJssdkPageUrl(rawUrl: string): string {
|
||||
try {
|
||||
const url = new URL(rawUrl);
|
||||
url.hash = '';
|
||||
url.searchParams.delete('code');
|
||||
url.searchParams.delete('state');
|
||||
const query = url.searchParams.toString();
|
||||
return `${url.origin}${url.pathname}${query ? `?${query}` : ''}`;
|
||||
} catch {
|
||||
return rawUrl.split('#')[0];
|
||||
}
|
||||
}
|
||||
|
||||
export function stripOAuthParamsFromLocation(): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
const url = new URL(window.location.href);
|
||||
if (!url.searchParams.has('code') && !url.searchParams.has('state')) return;
|
||||
url.searchParams.delete('code');
|
||||
url.searchParams.delete('state');
|
||||
const query = url.searchParams.toString();
|
||||
const next = `${url.pathname}${query ? `?${query}` : ''}${url.hash}`;
|
||||
window.history.replaceState({}, '', next);
|
||||
}
|
||||
|
||||
function loadScript(): Promise<void> {
|
||||
|
||||
@@ -75,13 +75,27 @@ export class WechatController {
|
||||
@Get('jssdk-config')
|
||||
async jssdkConfig(@Query('url') url: string, @Req() req: Request) {
|
||||
if (!url) throw new BadRequestException('url 参数必填');
|
||||
const pageUrl = decodeURIComponent(url).split('#')[0];
|
||||
const decoded = decodeURIComponent(url).split('#')[0];
|
||||
const pageUrl = this.normalizeJssdkUrl(decoded);
|
||||
const user = (req as Request & { user?: AuthUser }).user;
|
||||
const actorRef =
|
||||
user?.actorType === 'USER' ? { refType: 'USER', refId: user.actorId } : undefined;
|
||||
return this.wechat.createJssdkConfig(pageUrl, actorRef);
|
||||
}
|
||||
|
||||
private normalizeJssdkUrl(rawUrl: string): string {
|
||||
try {
|
||||
const parsed = new URL(rawUrl);
|
||||
parsed.hash = '';
|
||||
parsed.searchParams.delete('code');
|
||||
parsed.searchParams.delete('state');
|
||||
const query = parsed.searchParams.toString();
|
||||
return `${parsed.origin}${parsed.pathname}${query ? `?${query}` : ''}`;
|
||||
} catch {
|
||||
return rawUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@Get('oauth-url')
|
||||
oauthUrl(
|
||||
@Query('redirectUri') redirectUri: string,
|
||||
|
||||
Reference in New Issue
Block a user