微信SDK接通
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import type { WechatLoginPlatform, WechatLoginResult } from '@dukang/shared-types';
|
||||
import { getRuntimePlatform, isWechatBrowser } from './env';
|
||||
import type { WeixinSdkConfig } from './types';
|
||||
|
||||
const OAUTH_STATE_KEY = 'dukang_wx_oauth_state';
|
||||
|
||||
function randomState() {
|
||||
return `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
||||
}
|
||||
|
||||
async function apiRequest<T>(
|
||||
config: WeixinSdkConfig,
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Client-App': config.clientApp,
|
||||
...(options.headers as Record<string, string>),
|
||||
};
|
||||
const token = config.getAccessToken?.();
|
||||
if (token) headers.Authorization = `Bearer ${token}`;
|
||||
const apiBase = config.apiBase ?? '/api/v1';
|
||||
const res = await fetch(`${apiBase}${path}`, { ...options, headers });
|
||||
const json = await res.json();
|
||||
if (json.code !== 0) throw new Error(json.message || '请求失败');
|
||||
return json.data as T;
|
||||
}
|
||||
|
||||
/** 获取公众号 OAuth 授权跳转 URL */
|
||||
export async function getWechatOAuthUrl(
|
||||
config: WeixinSdkConfig,
|
||||
redirectUri: string,
|
||||
scope: 'snsapi_base' | 'snsapi_userinfo' = 'snsapi_userinfo',
|
||||
): Promise<string> {
|
||||
const state = randomState();
|
||||
sessionStorage.setItem(OAUTH_STATE_KEY, state);
|
||||
const qs = new URLSearchParams({ redirectUri, scope, state });
|
||||
const data = await apiRequest<{ url: string }>(config, `/common/wechat/oauth-url?${qs}`);
|
||||
return data.url;
|
||||
}
|
||||
|
||||
/** 发起微信 OAuth 登录(H5 公众号内跳转授权) */
|
||||
export async function startWechatOAuthLogin(config: WeixinSdkConfig, redirectUri?: string): Promise<void> {
|
||||
if (!isWechatBrowser()) {
|
||||
throw new Error('请在微信内打开');
|
||||
}
|
||||
const uri = redirectUri ?? window.location.href.split('#')[0];
|
||||
const url = await getWechatOAuthUrl(config, uri);
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
/** 小程序 wx.login 获取 code */
|
||||
export function getMiniProgramLoginCode(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!window.wx?.login) {
|
||||
reject(new Error('wx.login 不可用'));
|
||||
return;
|
||||
}
|
||||
window.wx.login({
|
||||
success: (res) => (res.code ? resolve(res.code) : reject(new Error('未获取到 code'))),
|
||||
fail: (err) => reject(new Error(err.errMsg || 'wx.login 失败')),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 用 code 完成微信登录(自动识别 H5 / 小程序) */
|
||||
export async function loginWithWechatCode(
|
||||
config: WeixinSdkConfig,
|
||||
code: string,
|
||||
platform?: WechatLoginPlatform,
|
||||
): Promise<WechatLoginResult> {
|
||||
const resolvedPlatform = platform ?? (getRuntimePlatform() === 'mini' ? 'mini' : 'h5');
|
||||
return apiRequest<WechatLoginResult>(config, '/auth/login/wechat', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ code, platform: resolvedPlatform }),
|
||||
});
|
||||
}
|
||||
|
||||
/** 处理 OAuth 回调 URL 中的 code 参数并登录 */
|
||||
export async function handleWechatOAuthCallback(
|
||||
config: WeixinSdkConfig,
|
||||
searchParams?: URLSearchParams,
|
||||
): Promise<WechatLoginResult | null> {
|
||||
const params = searchParams ?? new URLSearchParams(window.location.search);
|
||||
const code = params.get('code');
|
||||
if (!code) return null;
|
||||
|
||||
const state = params.get('state');
|
||||
const saved = sessionStorage.getItem(OAUTH_STATE_KEY);
|
||||
if (saved && state && saved !== state) {
|
||||
throw new Error('OAuth state 校验失败');
|
||||
}
|
||||
sessionStorage.removeItem(OAUTH_STATE_KEY);
|
||||
|
||||
return loginWithWechatCode(config, code, 'h5');
|
||||
}
|
||||
|
||||
/** 微信首登绑定手机号 */
|
||||
export async function bindWechatPhone(
|
||||
config: WeixinSdkConfig,
|
||||
payload: { wxSessionKey: string; phone: string; code: string },
|
||||
): Promise<WechatLoginResult> {
|
||||
return apiRequest<WechatLoginResult>(config, '/auth/wechat/bind-phone', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取微信手机号(小程序 getPhoneNumber 返回的 code,需后端解密) */
|
||||
export async function getWechatPhoneNumber(config: WeixinSdkConfig): Promise<string> {
|
||||
const platform = getRuntimePlatform();
|
||||
if (platform !== 'mini') {
|
||||
throw new Error('H5 环境请使用短信验证码绑定手机号');
|
||||
}
|
||||
const phoneCode = await new Promise<string>((resolve, reject) => {
|
||||
if (!window.wx?.getPhoneNumber) {
|
||||
reject(new Error('getPhoneNumber 不可用'));
|
||||
return;
|
||||
}
|
||||
window.wx.getPhoneNumber({
|
||||
success: (res) => (res.code ? resolve(res.code) : reject(new Error('未获取到手机号 code'))),
|
||||
fail: (err) => reject(new Error(err.errMsg || '获取手机号失败')),
|
||||
});
|
||||
});
|
||||
const data = await apiRequest<{ phone: string }>(config, '/common/wechat/phone-number', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ code: phoneCode, platform: 'mini' }),
|
||||
});
|
||||
return data.phone;
|
||||
}
|
||||
|
||||
/** 一键微信登录(自动选择 OAuth 或 wx.login) */
|
||||
export async function wechatLogin(config: WeixinSdkConfig): Promise<WechatLoginResult | void> {
|
||||
const platform = getRuntimePlatform();
|
||||
if (platform === 'mini') {
|
||||
const code = await getMiniProgramLoginCode();
|
||||
return loginWithWechatCode(config, code, 'mini');
|
||||
}
|
||||
if (platform === 'wechat-h5') {
|
||||
await startWechatOAuthLogin(config);
|
||||
return;
|
||||
}
|
||||
throw new Error('请在微信内打开以使用微信登录');
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/** 是否微信内置浏览器 */
|
||||
export function isWechatBrowser(): boolean {
|
||||
if (typeof navigator === 'undefined') return false;
|
||||
return /MicroMessenger/i.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
/** 是否微信小程序 web-view 或独立小程序环境 */
|
||||
export function isMiniProgram(): boolean {
|
||||
if (typeof window === 'undefined') return false;
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
return ua.includes('miniprogram') || (window as Window & { __wxjs_environment?: string }).__wxjs_environment === 'miniprogram';
|
||||
}
|
||||
|
||||
export function getRuntimePlatform(): 'mini' | 'wechat-h5' | 'browser' {
|
||||
if (isMiniProgram()) return 'mini';
|
||||
if (isWechatBrowser()) return 'wechat-h5';
|
||||
return 'browser';
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
export { isWechatBrowser, isMiniProgram, getRuntimePlatform } from './env';
|
||||
export { initWechatJssdk, ensureJssdkReady, isJssdkReady } from './jssdk';
|
||||
export {
|
||||
getWechatLocation,
|
||||
canUseWechatLocation,
|
||||
isWechatEnv,
|
||||
} from './location';
|
||||
export { scanQrCode } from './scan';
|
||||
export { invokeWechatPay } from './pay';
|
||||
export {
|
||||
getWechatOAuthUrl,
|
||||
startWechatOAuthLogin,
|
||||
getMiniProgramLoginCode,
|
||||
loginWithWechatCode,
|
||||
handleWechatOAuthCallback,
|
||||
bindWechatPhone,
|
||||
getWechatPhoneNumber,
|
||||
wechatLogin,
|
||||
} from './auth';
|
||||
export type { WeixinSdkConfig, WxApi, MiniProgramWx } from './types';
|
||||
export { DEFAULT_JS_API_LIST } from './types';
|
||||
|
||||
import type { WeixinSdkConfig } from './types';
|
||||
import { initWechatJssdk } from './jssdk';
|
||||
import { getWechatLocation } from './location';
|
||||
import { scanQrCode } from './scan';
|
||||
import { invokeWechatPay } from './pay';
|
||||
import {
|
||||
wechatLogin,
|
||||
handleWechatOAuthCallback,
|
||||
bindWechatPhone,
|
||||
getWechatPhoneNumber,
|
||||
} from './auth';
|
||||
|
||||
/** 微信 SDK 门面(按端注入 apiBase / clientApp) */
|
||||
export function createWeixinSdk(config: WeixinSdkConfig) {
|
||||
return {
|
||||
init: () => initWechatJssdk({ ...config, apiBase: config.apiBase ?? '/api/v1' }),
|
||||
login: () => wechatLogin(config),
|
||||
handleOAuthCallback: (params?: URLSearchParams) => handleWechatOAuthCallback(config, params),
|
||||
bindPhone: (payload: { wxSessionKey: string; phone: string; code: string }) =>
|
||||
bindWechatPhone(config, payload),
|
||||
getPhoneNumber: () => getWechatPhoneNumber(config),
|
||||
getLocation: () => getWechatLocation(config),
|
||||
scanQrCode: () => scanQrCode(config),
|
||||
pay: (prepay: Parameters<typeof invokeWechatPay>[0]) =>
|
||||
invokeWechatPay(prepay, { apiBase: config.apiBase, clientApp: config.clientApp }),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import type { WechatJssdkConfig } from '@dukang/shared-types';
|
||||
import { DEFAULT_JS_API_LIST } from './types';
|
||||
|
||||
const JSSDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
||||
|
||||
let scriptPromise: Promise<void> | null = null;
|
||||
let configured = false;
|
||||
|
||||
function loadScript(): Promise<void> {
|
||||
if (typeof document === 'undefined') return Promise.reject(new Error('非浏览器环境'));
|
||||
if (window.wx) return Promise.resolve();
|
||||
if (scriptPromise) return scriptPromise;
|
||||
|
||||
scriptPromise = new Promise((resolve, reject) => {
|
||||
const existing = document.querySelector<HTMLScriptElement>('script[data-dukang-wx-jssdk]');
|
||||
if (existing) {
|
||||
existing.addEventListener('load', () => resolve());
|
||||
existing.addEventListener('error', () => reject(new Error('微信 JSSDK 加载失败')));
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.src = JSSDK_URL;
|
||||
script.async = true;
|
||||
script.dataset.dukangWxJssdk = '1';
|
||||
script.onload = () => resolve();
|
||||
script.onerror = () => reject(new Error('微信 JSSDK 加载失败'));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
return scriptPromise;
|
||||
}
|
||||
|
||||
async function fetchJssdkConfig(apiBase: string, clientApp: string, url: string, token?: string | null) {
|
||||
const headers: Record<string, string> = { 'X-Client-App': clientApp };
|
||||
if (token) headers.Authorization = `Bearer ${token}`;
|
||||
const qs = new URLSearchParams({ url });
|
||||
const res = await fetch(`${apiBase}/common/wechat/jssdk-config?${qs}`, { headers });
|
||||
const json = await res.json();
|
||||
if (json.code !== 0) throw new Error(json.message || '获取 JSSDK 配置失败');
|
||||
return json.data as WechatJssdkConfig;
|
||||
}
|
||||
|
||||
/** 初始化微信 JSSDK(H5 公众号内) */
|
||||
export async function initWechatJssdk(options: {
|
||||
apiBase: string;
|
||||
clientApp: string;
|
||||
url?: string;
|
||||
getAccessToken?: () => string | null;
|
||||
jsApiList?: string[];
|
||||
}): Promise<void> {
|
||||
const { apiBase, clientApp, getAccessToken } = options;
|
||||
const pageUrl = options.url ?? (typeof window !== 'undefined' ? window.location.href.split('#')[0] : '');
|
||||
await loadScript();
|
||||
if (!window.wx) throw new Error('微信 JSSDK 不可用');
|
||||
|
||||
const config = await fetchJssdkConfig(apiBase, clientApp, pageUrl, getAccessToken?.());
|
||||
const jsApiList = options.jsApiList ?? [...DEFAULT_JS_API_LIST];
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
window.wx!.config({
|
||||
...config,
|
||||
jsApiList,
|
||||
debug: false,
|
||||
});
|
||||
window.wx!.ready(() => {
|
||||
configured = true;
|
||||
resolve();
|
||||
});
|
||||
window.wx!.error((err) => reject(new Error(err.errMsg || 'wx.config 失败')));
|
||||
});
|
||||
}
|
||||
|
||||
export function isJssdkReady(): boolean {
|
||||
return configured && !!window.wx;
|
||||
}
|
||||
|
||||
export async function ensureJssdkReady(options: {
|
||||
apiBase: string;
|
||||
clientApp: string;
|
||||
getAccessToken?: () => string | null;
|
||||
}): Promise<void> {
|
||||
if (!isJssdkReady()) {
|
||||
await initWechatJssdk(options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { WechatGpsLocation } from '@dukang/shared-types';
|
||||
import { getRuntimePlatform, isWechatBrowser } from './env';
|
||||
import { ensureJssdkReady } from './jssdk';
|
||||
import type { WeixinSdkConfig } from './types';
|
||||
|
||||
/** 获取 GPS 定位(微信 JSSDK / 小程序优先,否则 H5 Geolocation) */
|
||||
export async function getWechatLocation(config?: WeixinSdkConfig): Promise<WechatGpsLocation | null> {
|
||||
const platform = getRuntimePlatform();
|
||||
|
||||
if (platform === 'mini' && window.wx?.getLocation) {
|
||||
return new Promise((resolve) => {
|
||||
window.wx!.getLocation!({
|
||||
type: 'gcj02',
|
||||
success: (res) => resolve(res),
|
||||
fail: () => resolve(null),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (platform === 'wechat-h5' && config) {
|
||||
try {
|
||||
await ensureJssdkReady({
|
||||
apiBase: config.apiBase ?? '/api/v1',
|
||||
clientApp: config.clientApp,
|
||||
getAccessToken: config.getAccessToken,
|
||||
});
|
||||
if (window.wx?.getLocation) {
|
||||
return new Promise((resolve) => {
|
||||
window.wx!.getLocation!({
|
||||
type: 'gcj02',
|
||||
success: (res) => resolve(res),
|
||||
fail: () => resolve(null),
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
/* fall through */
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof navigator === 'undefined' || !navigator.geolocation) return null;
|
||||
return new Promise((resolve) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) =>
|
||||
resolve({
|
||||
latitude: pos.coords.latitude,
|
||||
longitude: pos.coords.longitude,
|
||||
accuracy: pos.coords.accuracy,
|
||||
}),
|
||||
() => resolve(null),
|
||||
{ enableHighAccuracy: false, timeout: 8000, maximumAge: 60_000 },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function canUseWechatLocation(): boolean {
|
||||
return getRuntimePlatform() !== 'browser' || typeof navigator?.geolocation !== 'undefined';
|
||||
}
|
||||
|
||||
export function isWechatEnv(): boolean {
|
||||
return isWechatBrowser() || getRuntimePlatform() === 'mini';
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { WechatJsapiPrepayParams } from '@dukang/shared-types';
|
||||
import { getRuntimePlatform } from './env';
|
||||
import { ensureJssdkReady } from './jssdk';
|
||||
|
||||
function invokeBridgePay(params: WechatJsapiPrepayParams): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bridge = window.WeixinJSBridge;
|
||||
if (!bridge) {
|
||||
reject(new Error('WeixinJSBridge 不可用'));
|
||||
return;
|
||||
}
|
||||
bridge.invoke(
|
||||
'getBrandWCPayRequest',
|
||||
{
|
||||
appId: params.appId,
|
||||
timeStamp: params.timeStamp,
|
||||
nonceStr: params.nonceStr,
|
||||
package: params.package,
|
||||
signType: params.signType,
|
||||
paySign: params.paySign,
|
||||
},
|
||||
(res) => {
|
||||
const msg = res.err_msg ?? '';
|
||||
if (msg.includes('ok')) resolve();
|
||||
else if (msg.includes('cancel')) reject(new Error('用户取消支付'));
|
||||
else reject(new Error(msg || '支付失败'));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/** 调起微信支付 */
|
||||
export async function invokeWechatPay(
|
||||
prepay: WechatJsapiPrepayParams,
|
||||
config?: { apiBase?: string; clientApp?: string },
|
||||
): Promise<void> {
|
||||
const platform = getRuntimePlatform();
|
||||
|
||||
if (platform === 'mini' && window.wx?.requestPayment) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.wx!.requestPayment!({
|
||||
timeStamp: prepay.timeStamp,
|
||||
nonceStr: prepay.nonceStr,
|
||||
package: prepay.package,
|
||||
signType: prepay.signType,
|
||||
paySign: prepay.paySign,
|
||||
success: () => resolve(),
|
||||
fail: (err) => reject(new Error(err.errMsg || '支付失败')),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (platform === 'wechat-h5') {
|
||||
if (config?.clientApp) {
|
||||
await ensureJssdkReady({
|
||||
apiBase: config.apiBase ?? '/api/v1',
|
||||
clientApp: config.clientApp,
|
||||
});
|
||||
}
|
||||
if (window.wx?.chooseWXPay) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.wx!.chooseWXPay!({
|
||||
...prepay,
|
||||
success: () => resolve(),
|
||||
fail: (err) => reject(new Error(err.errMsg || '支付失败')),
|
||||
cancel: () => reject(new Error('用户取消支付')),
|
||||
});
|
||||
});
|
||||
}
|
||||
if (window.WeixinJSBridge) {
|
||||
return invokeBridgePay(prepay);
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
await new Promise<void>((resolve) => {
|
||||
if (window.WeixinJSBridge) resolve();
|
||||
else document.addEventListener('WeixinJSBridgeReady', () => resolve(), { once: true });
|
||||
});
|
||||
return invokeBridgePay(prepay);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('当前环境不支持微信支付');
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { getRuntimePlatform } from './env';
|
||||
import { ensureJssdkReady } from './jssdk';
|
||||
import type { WeixinSdkConfig } from './types';
|
||||
|
||||
/** 调起扫码(返回二维码/条码内容,失败返回 null) */
|
||||
export async function scanQrCode(config: WeixinSdkConfig): Promise<string | null> {
|
||||
const platform = getRuntimePlatform();
|
||||
|
||||
if (platform === 'mini' && window.wx?.scanCode) {
|
||||
return new Promise((resolve) => {
|
||||
window.wx!.scanCode!({
|
||||
onlyFromCamera: true,
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => resolve(res.result || null),
|
||||
fail: () => resolve(null),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (platform === 'wechat-h5') {
|
||||
await ensureJssdkReady({
|
||||
apiBase: config.apiBase ?? '/api/v1',
|
||||
clientApp: config.clientApp,
|
||||
getAccessToken: config.getAccessToken,
|
||||
});
|
||||
if (window.wx?.scanQRCode) {
|
||||
return new Promise((resolve) => {
|
||||
window.wx!.scanQRCode!({
|
||||
needResult: 1,
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => resolve(res.resultStr || null),
|
||||
fail: () => resolve(null),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const manual = typeof window !== 'undefined' ? window.prompt('当前环境无法调起微信扫码,请手动输入核销码') : null;
|
||||
return manual?.trim() || null;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import type { WechatGpsLocation, WechatJsapiPrepayParams, WechatJssdkConfig } from '@dukang/shared-types';
|
||||
|
||||
export type WeixinSdkConfig = {
|
||||
/** API 根路径,默认 /api/v1 */
|
||||
apiBase?: string;
|
||||
/** X-Client-App 请求头 */
|
||||
clientApp: string;
|
||||
/** 获取 access token(可选,登录后自动带) */
|
||||
getAccessToken?: () => string | null;
|
||||
};
|
||||
|
||||
export type WxInvokeResult<T> = {
|
||||
errMsg: string;
|
||||
} & T;
|
||||
|
||||
export type WxApi = {
|
||||
config: (options: WechatJssdkConfig & { debug?: boolean }) => void;
|
||||
ready: (cb: () => void) => void;
|
||||
error: (cb: (res: { errMsg: string }) => void) => void;
|
||||
checkJsApi: (options: { jsApiList: string[]; success?: (res: Record<string, boolean>) => void }) => void;
|
||||
getLocation: (options: {
|
||||
type?: string;
|
||||
success?: (res: WechatGpsLocation) => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
}) => void;
|
||||
scanQRCode: (options: {
|
||||
needResult?: 0 | 1;
|
||||
scanType?: Array<'qrCode' | 'barCode'>;
|
||||
success?: (res: { resultStr: string }) => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
}) => void;
|
||||
chooseWXPay: (options: WechatJsapiPrepayParams & {
|
||||
success?: () => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
cancel?: () => void;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
export type MiniProgramWx = {
|
||||
login: (options: {
|
||||
success?: (res: { code: string }) => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
}) => void;
|
||||
getPhoneNumber: (options: {
|
||||
success?: (res: { code: string }) => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
}) => void;
|
||||
getLocation: WxApi['getLocation'];
|
||||
scanCode: (options: {
|
||||
onlyFromCamera?: boolean;
|
||||
scanType?: Array<'qrCode' | 'barCode'>;
|
||||
success?: (res: { result: string }) => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
}) => void;
|
||||
requestPayment: (options: {
|
||||
timeStamp: string;
|
||||
nonceStr: string;
|
||||
package: string;
|
||||
signType: string;
|
||||
paySign: string;
|
||||
success?: () => void;
|
||||
fail?: (res: { errMsg: string }) => void;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
wx?: WxApi & MiniProgramWx;
|
||||
WeixinJSBridge?: {
|
||||
invoke: (
|
||||
api: string,
|
||||
params: Record<string, unknown>,
|
||||
cb: (res: { err_msg?: string }) => void,
|
||||
) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const DEFAULT_JS_API_LIST = [
|
||||
'getLocation',
|
||||
'scanQRCode',
|
||||
'chooseWXPay',
|
||||
'updateAppMessageShareData',
|
||||
'updateTimelineShareData',
|
||||
] as const;
|
||||
Reference in New Issue
Block a user