Files
dukang/apps/mini-user/src/lib/wechat-auth.ts
T
jacy 1ad49f1acf
CI / verify (pull_request) Has been cancelled
h5微信授权
2026-07-14 20:09:36 +08:00

125 lines
4.2 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 { WechatLoginResult } from '@dukang/shared-types';
import { isWxAuthorizeEnabled } from '@dukang/shared-types';
import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk';
import Taro from '@tarojs/taro';
import { saveAuth } from './api';
import { syncMiniWechatProfile } from './mini-wechat-profile';
import {
authorizeWechatForPay,
fetchClientConfig,
fetchUserProfile,
needsWechatAuthForPay,
saveWechatLoginResult,
type WechatBindResult,
} from './pay-wechat';
import { isWechatEnv, weixinSdk } from './weixin';
export type WechatAuthEnsureResult =
| { ok: true }
| { ok: false; redirecting: true }
| { ok: false; needBindPhone: true; wxSessionKey: string };
/** 小程序:Taro.login → /auth/login/wechatH5:公众号 OAuth */
export async function loginWithWechat(): Promise<WechatLoginResult | void> {
if (process.env.TARO_ENV === 'h5') {
return loginWithWechatSdk();
}
const res = await Taro.login();
if (!res.code) {
throw new Error(res.errMsg || '微信登录失败,未获取到 code');
}
const { request } = await import('./api');
return request<WechatLoginResult>('/auth/login/wechat', {
method: 'POST',
data: { code: res.code, platform: 'mini' },
});
}
export async function checkNeedsWechatAuth(): Promise<boolean> {
const [config, profile] = await Promise.all([fetchClientConfig(), fetchUserProfile()]);
return needsWechatAuthForPay(config, profile);
}
/** 真实微信支付前确保已绑定微信;OAuth 跳转时返回 redirecting */
export async function ensureWechatAuthForPay(): Promise<WechatAuthEnsureResult> {
if (!isWechatEnv()) return { ok: true };
if (!(await checkNeedsWechatAuth())) return { ok: true };
const result = await authorizeWechatForPay();
if (!result) return { ok: false, redirecting: true };
if (result.needBindPhone && result.wxSessionKey) {
return { ok: false, needBindPhone: true, wxSessionKey: result.wxSessionKey };
}
if (saveWechatLoginResult(result)) {
return { ok: true };
}
return { ok: false, redirecting: true };
}
/** 处理 URL 中 OAuth ?code= 回调(H5 公众号) */
export async function handleWechatAuthCallback(): Promise<WechatLoginResult | null> {
if (process.env.TARO_ENV !== 'h5') return null;
if (!isWechatEnv()) return null;
const config = await fetchClientConfig();
if (!isWxAuthorizeEnabled(config)) return null;
const result = await weixinSdk.handleOAuthCallback();
if (result) stripOAuthParamsFromLocation();
return result;
}
export async function loginWithWechatSdk(): Promise<WechatLoginResult | void> {
const config = await fetchClientConfig();
if (!isWxAuthorizeEnabled(config)) return;
if (!isWechatEnv()) {
throw new Error('请在微信内打开以使用微信一键授权');
}
return weixinSdk.login();
}
export function applyWechatLoginResult(result: WechatLoginResult): boolean {
return saveWechatLoginResult(result);
}
/** 已登录用户绑定微信:小程序 code;H5 走公众号 OAuth(带 JWT 时服务端会 attach */
export async function bindWechatForUser(
prefetchedWxProfile?: { nickname?: string; avatarUrl?: string } | null,
): Promise<WechatBindResult> {
if (process.env.TARO_ENV === 'h5') {
const result = await authorizeWechatForPay();
if (!result) {
return { ok: false, redirecting: true };
}
if (result.needBindPhone && result.wxSessionKey) {
return { ok: false, needBindPhone: true, wxSessionKey: result.wxSessionKey };
}
if (result.accessToken) {
saveAuth({
accessToken: result.accessToken,
refreshToken: result.refreshToken,
});
}
const profile = await fetchUserProfile();
return { ok: true, profile };
}
const res = await Taro.login();
if (!res.code) {
throw new Error(res.errMsg || '微信授权失败');
}
const { request } = await import('./api');
const data = await request<WechatLoginResult>('/auth/wechat/bind', {
method: 'POST',
data: { code: res.code, platform: 'mini' },
});
if (data.needBindPhone && data.wxSessionKey) {
return { ok: false, needBindPhone: true, wxSessionKey: data.wxSessionKey };
}
await syncMiniWechatProfile(prefetchedWxProfile);
const profile = await fetchUserProfile();
return { ok: true, profile };
}
export type { WechatBindResult };