Files
dukang/apps/h5-shop/src/lib/wechat-auth.ts
T
jacy 22845e5d5f
CI / verify (pull_request) Has been cancelled
门店端增加绑定微信按钮
2026-07-16 08:30:04 +08:00

137 lines
4.7 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 type { ClientRuntimeConfig, WechatLoginResult } from '@dukang/shared-types';
import { isWxAuthorizeEnabled } from '@dukang/shared-types';
import { WECHAT_INAPP_REQUIRED_MSG } from '@dukang/weixin-sdk';
import { isWechatEnv, weixinSdk } from './weixin';
import { request, saveWechatSession, type ShopSessionPayload } from './api';
export type ShopAccountProfile = {
id: string;
storeId: string;
name: string;
phone: string;
/** 是否已绑定微信(来自 /shop/auth/me account.hasWechat */
hasWechat?: boolean;
wxOpenId?: string | null;
store?: { name: string };
};
export async function fetchClientConfig(): Promise<ClientRuntimeConfig> {
return request<ClientRuntimeConfig>('SHOP_H5', '/common/client-config');
}
export async function fetchShopAccount(): Promise<ShopAccountProfile> {
const me = await request<{
id?: string;
storeId?: string;
name?: string;
phone?: string;
account?: {
id?: string;
name?: string;
phone?: string;
hasWechat?: boolean;
};
store?: { name?: string } | null;
}>('SHOP_H5', '/shop/auth/me');
return {
id: String(me.account?.id ?? me.id ?? ''),
storeId: String(me.storeId ?? ''),
name: String(me.account?.name ?? me.name ?? ''),
phone: String(me.account?.phone ?? me.phone ?? ''),
hasWechat: !!me.account?.hasWechat,
store: me.store?.name ? { name: String(me.store.name) } : undefined,
};
}
export function needsWechatAuth(
profile: ShopAccountProfile | null,
config?: Pick<ClientRuntimeConfig, 'wxAuthorize'> | null,
): boolean {
if (config && !isWxAuthorizeEnabled(config)) return false;
const unbound = !!profile && !(profile.hasWechat || profile.wxOpenId);
return isWechatEnv() && unbound;
}
export async function checkNeedsWechatAuth(profile: ShopAccountProfile | null): Promise<boolean> {
const config = await fetchClientConfig();
return needsWechatAuth(profile, config);
}
export function sessionFromWechatLogin(result: WechatLoginResult): ShopSessionPayload | null {
if (!result.accessToken || !result.refreshToken) return null;
const store = result.store as Record<string, unknown> | undefined;
const stores = Array.isArray((result as { stores?: unknown }).stores)
? ((result as { stores: ShopSessionPayload['stores'] }).stores)
: undefined;
const account = (result as { account?: ShopSessionPayload['account'] }).account;
return {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
stores,
account,
selectedStoreId: (result as { selectedStoreId?: string }).selectedStoreId,
store: store
? {
id: String(store.id ?? account?.id ?? ''),
storeId: String(store.storeId ?? ''),
name: String(store.name ?? account?.name ?? ''),
phone: String(store.phone ?? account?.phone ?? ''),
storeName: String(store.storeName ?? store.name ?? ''),
isPrimary: account?.isPrimary,
stores,
}
: undefined,
};
}
/** 处理微信登录/绑定结果,写入 7 天免登录 session */
export function handleShopWechatLoginResult(result: WechatLoginResult): ShopSessionPayload | null {
const session = sessionFromWechatLogin(result);
if (!session) return null;
saveWechatSession(session);
return session;
}
/** @deprecated 使用 handleShopWechatLoginResult */
export function saveShopWechatAuth(result: WechatLoginResult): boolean {
return !!handleShopWechatLoginResult(result);
}
export async function handleShopWechatCallback(): Promise<WechatLoginResult | null> {
if (!isWechatEnv()) return null;
const config = await fetchClientConfig();
if (!isWxAuthorizeEnabled(config)) return null;
return weixinSdk.handleOAuthCallback();
}
/**
* 微信一键登录(已绑定微信的门店账号免验证码)。
* 返回 session = 已登录;void = 已跳转授权页等待回调。
*/
export async function loginShopWithWechat(): Promise<ShopSessionPayload | null | void> {
const config = await fetchClientConfig();
if (!isWxAuthorizeEnabled(config)) return null;
if (!isWechatEnv()) {
throw new Error(WECHAT_INAPP_REQUIRED_MSG);
}
const result = await weixinSdk.login();
if (result) return handleShopWechatLoginResult(result);
}
/** 短信登录成功后于微信内自动发起 OAuth,绑定 openId 便于后续免登 */
export async function bindShopWechatAfterSmsLogin(): Promise<void> {
const config = await fetchClientConfig();
if (!isWxAuthorizeEnabled(config)) return;
if (!isWechatEnv()) return;
await weixinSdk.login();
}
export async function authorizeShopWechat(): Promise<WechatLoginResult | void> {
const config = await fetchClientConfig();
if (!isWxAuthorizeEnabled(config)) return;
if (!isWechatEnv()) {
throw new Error(WECHAT_INAPP_REQUIRED_MSG);
}
return weixinSdk.login();
}