import { cryptoFramework } from "@kit.CryptoArchitectureKit"; import { LogUtil, PreferencesUtil, StrUtil, ToastUtil } from "@pura/harmony-utils"; import { CommonConstants, STR_LOCK_VIDEO, VIP_FILEPATH } from "../constants/CommonConstants"; import { http } from "@kit.NetworkKit"; import { bundleManager } from "@kit.AbilityKit"; import { BusinessError } from '@kit.BasicServicesKit'; import { hilog } from "@kit.PerformanceAnalysisKit"; import { FileUtil } from "@pura/harmony-utils"; import { fileUri, picker } from "@kit.CoreFileKit"; import { VipPage } from "../../pages/VipPage"; // 用户相关接口定义 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 ApiResponse { code: number; msg: string; data?: WechatUserInfoData; } 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; hasCome: boolean; } export interface VipPlanApi { id: string; name: string; description: string; price: string; original_price:string; discount_percent:number; duration: string; features: string; sort_order: string; status: string; created_at: string; updated_at: string; } export interface VipPlanListApiData { plans: VipPlanApi[]; show_wx_login?: boolean; } 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; 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; 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(): Promise { try { let token=PreferencesUtil.getStringSync('userToken',''); 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; let json: UserInfoApiResponse = JSON.parse(res) as UserInfoApiResponse; if (json.code === 0 && json.data) { const userId = json.data.system_info?.user_id ? Number(json.data.system_info?.user_id) : 0; 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 = ''; let plan_id = ''; 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; plan_id = subInfo.subscriptions[0].plan_id } // 保存登录状态 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 || ''); if(plan_id=='1'){ PreferencesUtil.putSync('isForever', true); }else{ PreferencesUtil.putSync('isForever', false); } return json.data; } else { return null; } } else { return null; } } catch (e) { return null; } } /** * 退出登录 * 清除用户登录状态和相关信息 */ static logout(): boolean { // 清除登录状态 PreferencesUtil.deleteSync('isLogin'); PreferencesUtil.deleteSync('userId'); PreferencesUtil.deleteSync('userName'); PreferencesUtil.deleteSync('userAvatarUrl'); PreferencesUtil.deleteSync('subscriptionName'); PreferencesUtil.deleteSync('subscriptionEndDate'); PreferencesUtil.deleteSync('hasActiveSubscription'); PreferencesUtil.deleteSync('userToken'); PreferencesUtil.deleteSync('isForever'); // 显示退出登录提示 return true; } /** * 本地会员同步到服务器 * @param expireDate * @returns */ static async syncLocalNobleToServer(expireDate: string): Promise { try { const token = PreferencesUtil.getStringSync('userToken', ''); if (!token || !expireDate) { ToastUtil.showToast('缺少token或到期时间'); return false; } const url = `https://pay.ss5.xyz/user/sync_noble?token=${encodeURIComponent(token)}&expireDate=${encodeURIComponent(expireDate)}`; 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; let json = JSON.parse(res) as ApiResponse; if (json.code === 0) { ToastUtil.showToast('会员同步成功'); PreferencesUtil.putSync('isNoble', false); PreferencesUtil.putSync('nobleExpireDate', ''); // 删除本地会员文件 try { const documentViewPicker = new picker.DocumentViewPicker() let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD }) let download_path = new fileUri.FileUri(documentSaveResult[0]).path + '/' let filePath = download_path + STR_LOCK_VIDEO+ '/' + VIP_FILEPATH if (FileUtil.accessSync(filePath)) { FileUtil.unlink(filePath); } } catch (e) { console.log("Heanup exception="+e) // 可忽略或打印日志 } return true; } else { ToastUtil.showToast('会员同步失败:' + (json.msg || '未知错误')); return false; } } else { ToastUtil.showToast('会员同步接口请求失败'); return false; } } catch (e) { ToastUtil.showToast('会员同步异常' + (e && e.message ? (': ' + e.message) : '')); return false; } } }