import { cryptoFramework } from "@kit.CryptoArchitectureKit"; import { LogUtil, PreferencesUtil, StrUtil, ToastUtil } from "@pura/harmony-utils"; import { CommonConstants } from "../constants/CommonConstants"; import { http } from "@kit.NetworkKit"; import { bundleManager } from "@kit.AbilityKit"; import { BusinessError } from '@kit.BasicServicesKit'; import { hilog } from "@kit.PerformanceAnalysisKit"; // 用户相关接口定义 export interface WechatUserInfo { openid?: string; nickname?: string; sex?: number; language?: string; city?: string; province?: string; country?: string; headimgurl?: string; privilege?: string[]; unionid?: string; } export interface Subscription { plan_id: string; plan_name: string; plan_description: string; price: string; start_date: string; end_date: string; status: string; auto_renew: string; features: string[]; } export interface SubscriptionInfo { has_active_subscription: boolean; subscriptions: Subscription[]; } export interface responseData { token_info?: object; user_info: WechatUserInfo; } export interface WechatUserInfoData { token_info?: object; user_info: WechatUserInfo; system_info?: SystemInfo; subscription_info?: SubscriptionInfo; } export interface WechatUserInfoApiResponse { code: number; msg: string; data: WechatUserInfoData; } export interface HuaweiUserInfo { openid: string; unionid?: string; nickname: string; headimgurl: string; } export interface SystemInfo { user_id: number | string; token: string; } export interface HuaweiUserInfoApiData { user_info: HuaweiUserInfo; system_info?: SystemInfo; subscription_info?: SubscriptionInfo; } export interface HuaweiUserInfoApiResponse { code: number; msg: string; data: HuaweiUserInfoApiData; } export interface VipFeature { icon: Resource; title: string; description: string; isVipOnly: boolean; } export interface VipPlanApi { id: string; name: string; description: string; price: string; duration: string; features: string; sort_order: string; status: string; created_at: string; updated_at: string; } export interface VipPlanListApiData { plans: VipPlanApi[]; } export interface VipPlanListApiResponse { code: number; msg: string; data: VipPlanListApiData; } export interface WeChatPrepayId { prepay_id: string; } export interface UserInfoApiData { user_info: WechatUserInfo; system_info: SystemInfo; subscription_info: SubscriptionInfo; } export interface UserInfoApiResponse { code: number; msg: string; data: UserInfoApiData; } export default class UserUtil { // 微信是否安装 static async isHasWX(): Promise { try { let link = 'weixin://'; let data = bundleManager.canOpenLink(link); if (data) { return true; } else { ToastUtil.showToast('微信未安装'); return false; } } catch (err) { let message = (err as BusinessError).message; console.error('canOpenLink failed: %{public}s', message); return false; } } // 创建微信订单 static async createWeChatOrder(app_id: string, mch_id: string, description: string, amount: number, out_trade_no_pre: string, uid: number, plan_id: string): Promise { let requestUrl = CommonConstants.WX_PAY_API + '?app_id=' + encodeURIComponent(app_id.trim()) + '&mch_id=' + encodeURIComponent(mch_id.trim()) + '&description=' + encodeURIComponent(description.trim()) + '&amount=' + encodeURIComponent(amount) + '&uid=' + encodeURIComponent(uid) + '&plan_id=' + encodeURIComponent(plan_id) + '&out_trade_no_pre=' + encodeURIComponent(out_trade_no_pre); const httpRequest: http.HttpRequest = http.createHttp(); const options: http.HttpRequestOptions = { method: http.RequestMethod.GET, readTimeout: 6000, connectTimeout: 6000, }; try { const response: http.HttpResponse = await httpRequest.request(requestUrl, options); if (response.responseCode === 200) { let responseData: WeChatPrepayId; if (typeof response.result === 'string') { responseData = JSON.parse(response.result) as WeChatPrepayId; } else if (response.result instanceof Object) { responseData = response.result as WeChatPrepayId; } else { throw new Error('Unexpected response format'); } const prepayId: string = responseData.prepay_id; return prepayId; } else { ToastUtil.showToast('微信下单失败'); return ''; } } catch (error) { ToastUtil.showToast('微信下单异常'); return ''; } } // 构造签名串 static constructSignatureString(appId: string, timestamp: string, nonceStr: string, prepayId: string): string { return `${appId}\n${timestamp}\n${nonceStr}\n${prepayId}\n`; } // 微信支付签名 static async rsaSign2048( message: string): Promise { try { let keyGen = cryptoFramework.createAsyKeyGenerator("RSA2048") let key = keyGen.convertPemKeySync(null, CommonConstants.WX_PAY_RSA_PRIVATE_KEY) let sign = cryptoFramework.createSign('RSA2048|PKCS1|SHA256'); let msgBlob: cryptoFramework.DataBlob = {data: StrUtil.strToUint8Array(message)} sign.initSync(key.priKey) let signature = await sign.sign(msgBlob) return signature.data } catch (error) { console.error(error, `onecold error code: ${error.code}`) return new Uint8Array() } } // 通过code换取用户信息 static async getUserInfoByCode(code: string): Promise { try { const url = `https://pay.ss5.xyz/wechat/user_info?code=${encodeURIComponent(code)}`; const httpRequest = http.createHttp(); const options: http.HttpRequestOptions = { method: http.RequestMethod.GET, readTimeout: 3000, connectTimeout: 3000, }; const response: http.HttpResponse = await httpRequest.request(url, options); if (response.responseCode === 200) { let res = response.result as string; LogUtil.debug("getUserInfoByCode res =" + res) let json: WechatUserInfoApiResponse = JSON.parse(res) as WechatUserInfoApiResponse; if (json.code === 0 && json.data && json.data.user_info) { const userId = json.data.system_info?.user_id ? Number(json.data.system_info?.user_id) : 0; hilog.info(0x0000, 'UserUtil', `getUserInfoByCode: userId set to ${userId}`); const userName = json.data.user_info.nickname || '微信用户'; const userAvatarUrl = json.data.user_info.headimgurl && json.data.user_info.headimgurl.length > 0 ? json.data.user_info.headimgurl : ''; // 解析订阅信息 const subInfo = json.data.subscription_info; let hasActiveSubscription = false; let subscriptionName = ''; let subscriptionEndDate = ''; if (subInfo && subInfo.has_active_subscription && subInfo.subscriptions && subInfo.subscriptions.length > 0) { hasActiveSubscription = true; subscriptionName = subInfo.subscriptions[0].plan_name; subscriptionEndDate = subInfo.subscriptions[0].end_date; } // 保存登录状态和用户信息 PreferencesUtil.putSync('isLogin', true); PreferencesUtil.putSync('userId', userId); PreferencesUtil.putSync('userName', userName); PreferencesUtil.putSync('userAvatarUrl', userAvatarUrl); PreferencesUtil.putSync('subscriptionName', subscriptionName); PreferencesUtil.putSync('subscriptionEndDate', subscriptionEndDate); PreferencesUtil.putSync('hasActiveSubscription', hasActiveSubscription); PreferencesUtil.putSync('userToken', json.data.system_info?.token || ''); ToastUtil.showToast('微信登录成功'); return json.data; } else { ToastUtil.showToast('用户信息解析失败'); return null; } } else { ToastUtil.showToast('获取用户信息失败'); return null; } } catch (e) { ToastUtil.showToast('登录失败' + (e && e.message ? (': ' + e.message) : '')); return null; } } // 通过华为账号信息换取用户信息和会员状态 static async getUserInfoByHuawei(openID: string, unionID: string, nickname: string, avatarUri: string): Promise { try { const url = `https://pay.ss5.xyz/user/huawei?openID=${encodeURIComponent(openID)}&unionID=${encodeURIComponent(unionID)}&nickname=${encodeURIComponent(nickname)}&avatarUri=${encodeURIComponent(avatarUri)}`; const httpRequest = http.createHttp(); const options: http.HttpRequestOptions = { method: http.RequestMethod.GET, readTimeout: 3000, connectTimeout: 3000, }; const response: http.HttpResponse = await httpRequest.request(url, options); if (response.responseCode === 200) { let res = response.result as string; LogUtil.debug("getUserInfoByHuawei res =" + res) let json: HuaweiUserInfoApiResponse = JSON.parse(res) as HuaweiUserInfoApiResponse; if (json.code === 0 && json.data && json.data.user_info) { const userId = json.data.system_info?.user_id ? Number(json.data.system_info?.user_id) : 0; hilog.info(0x0000, 'UserUtil', `getUserInfoByHuawei: userId set to ${userId}`); const userName = json.data.user_info.nickname || '华为用户'; const userAvatarUrl = json.data.user_info.headimgurl || ''; // 处理会员信息 const subInfo = json.data.subscription_info; let hasActiveSubscription = false; let subscriptionName = ''; let subscriptionEndDate = ''; if (subInfo && subInfo.has_active_subscription && subInfo.subscriptions && subInfo.subscriptions.length > 0) { hasActiveSubscription = true; subscriptionName = subInfo.subscriptions[0].plan_name; subscriptionEndDate = subInfo.subscriptions[0].end_date; } // 保存登录状态 PreferencesUtil.putSync('isLogin', true); PreferencesUtil.putSync('userId', userId); PreferencesUtil.putSync('userName', userName); PreferencesUtil.putSync('userAvatarUrl', userAvatarUrl); PreferencesUtil.putSync('subscriptionName', subscriptionName); PreferencesUtil.putSync('subscriptionEndDate', subscriptionEndDate); PreferencesUtil.putSync('hasActiveSubscription', hasActiveSubscription); PreferencesUtil.putSync('userToken', json.data.system_info?.token || ''); ToastUtil.showToast('华为登录成功'); return json.data; } else { ToastUtil.showToast('用户信息解析失败'); return null; } } else { ToastUtil.showToast('获取用户信息失败'); return null; } } catch (e) { ToastUtil.showToast('登录失败' + (e && e.message ? (': ' + e.message) : '')); return null; } } // 刷新用户信息 static async fetchUserInfo(token: string): Promise { try { const url = `https://pay.ss5.xyz/user/info?token=${encodeURIComponent(token)}`; const httpRequest = http.createHttp(); const options: http.HttpRequestOptions = { method: http.RequestMethod.GET, readTimeout: 3000, connectTimeout: 3000, }; const response: http.HttpResponse = await httpRequest.request(url, options); if (response.responseCode === 200) { let res = response.result as string; LogUtil.debug("fetchUserInfo res =" + res); let json: UserInfoApiResponse = JSON.parse(res) as UserInfoApiResponse; if (json.code === 0 && json.data) { const userInfo = json.data.user_info; const systemInfo = json.data.system_info; const subInfo = json.data.subscription_info; let hasActiveSubscription = false; let subscriptionName = ''; let subscriptionEndDate = ''; if (subInfo && subInfo.has_active_subscription && subInfo.subscriptions && subInfo.subscriptions.length > 0) { hasActiveSubscription = true; subscriptionName = subInfo.subscriptions[0].plan_name; subscriptionEndDate = subInfo.subscriptions[0].end_date; } // ToastUtil.showToast('用户信息已更新'); return json.data; } else { ToastUtil.showToast('刷新用户信息失败'); return null; } } else { ToastUtil.showToast('获取用户信息失败'); return null; } } catch (e) { ToastUtil.showToast('获取用户信息异常' + (e && e.message ? (': ' + e.message) : '')); hilog.error(0x0000, 'UserUtil', `fetchUserInfo error: ${e.message}`); return null; } } // 华为登录错误处理 static dealHuaweiLoginError(error: BusinessError): void { if (error.code === 1001) { ToastUtil.showToast('未登录华为账号'); } else if (error.code === 1002) { ToastUtil.showToast('网络异常'); } else if (error.code === 1003) { ToastUtil.showToast('内部错误'); } else if (error.code === 1004) { ToastUtil.showToast('用户取消授权'); } else if (error.code === 1005) { ToastUtil.showToast('系统服务异常'); } else if (error.code === 1006) { ToastUtil.showToast('请求被拒绝'); } else if (error.code === 1007) { ToastUtil.showToast('无权限'); } else { ToastUtil.showToast('登录失败'); } } }