cashier/utils/merchant-context.ts

172 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import CryptoJS from 'crypto-js';
import { encryptedStorage } from '@/utils/storage';
export const ACTIVE_QR_URL_CACHE_KEY = 'cashier:property:active-url';
export const ACTIVE_QR_HASH_CACHE_KEY = 'cashier:property:active-url-hash';
export const PENDING_MERCHANT_URL_KEY = 'cashier:pending-merchant-url';
const decodeOption = (value?: string) => {
if (!value) return '';
try {
return decodeURIComponent(value);
} catch (error) {
return value;
}
};
export const resolveMerchantUrlFromOptions = (options: Record<string, string>) => {
return decodeOption(options.url || options.qr_url || '');
};
const readQueryParam = (raw: string, key: string) => {
const match = raw.match(new RegExp(`[?&]${key}=([^&]+)`));
return match ? decodeOption(match[1]) : '';
};
export const resolveMerchantUrlFromScanResult = (raw: string) => {
const text = (raw || '').trim();
if (!text) return '';
if (/^https?:\/\//i.test(text)) {
return text;
}
const fromUrl = readQueryParam(text, 'url') || readQueryParam(text, 'qr_url');
if (fromUrl) return fromUrl;
const fromQ = readQueryParam(text, 'q');
if (fromQ) return resolveMerchantUrlFromScanResult(fromQ);
return '';
};
export const setPendingMerchantUrl = (merchantUrl: string) => {
if (!merchantUrl) return;
encryptedStorage.set(PENDING_MERCHANT_URL_KEY, merchantUrl);
// @ts-ignore
if (uni.$globalData) {
// @ts-ignore
uni.$globalData.pendingMerchantUrl = merchantUrl;
}
};
export const getPendingMerchantUrl = () => {
// @ts-ignore
const fromMemory = uni.$globalData?.pendingMerchantUrl;
if (fromMemory) return String(fromMemory);
return encryptedStorage.get<string>(PENDING_MERCHANT_URL_KEY) || '';
};
export const clearPendingMerchantUrl = () => {
encryptedStorage.remove(PENDING_MERCHANT_URL_KEY);
// @ts-ignore
if (uni.$globalData) {
// @ts-ignore
uni.$globalData.pendingMerchantUrl = '';
}
};
const resolveMerchantUrlFromRawEntry = (raw?: string) => {
if (!raw) return '';
return resolveMerchantUrlFromScanResult(decodeOption(raw));
};
const getLaunchQuery = () => {
try {
const launchOptions = uni.getLaunchOptionsSync?.();
return (launchOptions?.query || {}) as Record<string, string>;
} catch (error) {
return {};
}
};
/** App.onLaunch / App.onShow捕获支付宝普通二维码 qrCode */
export const captureMerchantUrlFromLaunchOptions = (options: Record<string, any> = {}) => {
const query = options.query || {};
const qrCode = query.qrCode || query.qr_code || '';
if (!qrCode) return '';
const merchantUrl = resolveMerchantUrlFromRawEntry(String(qrCode));
if (!merchantUrl) return '';
setPendingMerchantUrl(merchantUrl);
return merchantUrl;
};
/**
* 统一解析进页 merchantUrl
* 1. ?url= / ?qr_url=(开发调试、内部跳转)
* 2. 微信普通二维码 options.q
* 3. 支付宝 query.qrCode页面参数 / getLaunchOptionsSync
* 4. App 缓存 pendingMerchantUrl
*/
export const resolveMerchantUrlFromEntry = (options: Record<string, any> = {}) => {
const direct = resolveMerchantUrlFromOptions(options as Record<string, string>);
if (direct) {
const merchantUrl = resolveMerchantUrlFromScanResult(direct);
if (merchantUrl) return merchantUrl;
}
if (options.q) {
const fromQ = resolveMerchantUrlFromRawEntry(String(options.q));
if (fromQ) return fromQ;
}
const pageQrCode = options.qrCode || options.qr_code;
if (pageQrCode) {
const fromPageQr = resolveMerchantUrlFromRawEntry(String(pageQrCode));
if (fromPageQr) return fromPageQr;
}
const launchQuery = getLaunchQuery();
const launchQrCode = launchQuery.qrCode || launchQuery.qr_code;
if (launchQrCode) {
const fromLaunch = resolveMerchantUrlFromRawEntry(String(launchQrCode));
if (fromLaunch) return fromLaunch;
}
return getPendingMerchantUrl();
};
export const getMerchantUrlHash = (merchantUrl: string) => {
return CryptoJS.SHA256(merchantUrl).toString();
};
export const getPropertyBindingCacheKeyByHash = (hash: string) => {
// Hash key keeps cache key short/stable and avoids raw URL special characters.
return `cashier:property:binding:${hash}`;
};
export const getSupplierCacheKeyByHash = (hash: string) => {
return `cashier:supplier:detail:${hash}`;
};
export const getPropertyBindingCacheKey = (merchantUrl: string) => {
return getPropertyBindingCacheKeyByHash(getMerchantUrlHash(merchantUrl));
};
export const getSupplierCacheKey = (merchantUrl: string) => {
return getSupplierCacheKeyByHash(getMerchantUrlHash(merchantUrl));
};
export const syncActiveMerchantContext = (merchantUrl: string) => {
const currentHash = getMerchantUrlHash(merchantUrl);
const previousHash = encryptedStorage.get<string>(ACTIVE_QR_HASH_CACHE_KEY) || '';
encryptedStorage.set(ACTIVE_QR_URL_CACHE_KEY, merchantUrl);
encryptedStorage.set(ACTIVE_QR_HASH_CACHE_KEY, currentHash);
return {
currentHash,
previousHash,
switched: Boolean(previousHash && previousHash !== currentHash)
};
};
export const getActiveMerchantUrl = () => encryptedStorage.get<string>(ACTIVE_QR_URL_CACHE_KEY) || '';
export const clearActiveMerchantContext = () => {
encryptedStorage.remove(ACTIVE_QR_URL_CACHE_KEY);
encryptedStorage.remove(ACTIVE_QR_HASH_CACHE_KEY);
};