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/wechat;H5:公众号 OAuth */ export async function loginWithWechat(): Promise { 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('/auth/login/wechat', { method: 'POST', data: { code: res.code, platform: 'mini' }, }); } export async function checkNeedsWechatAuth(): Promise { const [config, profile] = await Promise.all([fetchClientConfig(), fetchUserProfile()]); return needsWechatAuthForPay(config, profile); } /** 真实微信支付前确保已绑定微信;OAuth 跳转时返回 redirecting */ export async function ensureWechatAuthForPay(): Promise { 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 { 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 { 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 { 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('/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 };