import { promptAction, router, window } from '@kit.ArkUI'; import TitleBar from '../view/TitleBar'; import { webview } from '@kit.ArkWeb'; import { CommonConstants } from '../common/constants/CommonConstants'; import { AES, AppUtil, Base64Util, CryptoHelper, DateUtil, FileUtil, LogUtil, PreferencesUtil, RandomUtil, StrUtil, ToastUtil } from '@pura/harmony-utils'; import { BusinessError } from '@kit.BasicServicesKit'; import { Pay } from '@cashier_alipay/cashiersdk'; import { OrderInfoUtil } from '../alipay/OrderInfoUtil'; import { Utility } from '../common/util/Utility'; import fileIo, { ReadTextOptions } from '@ohos.file.fs'; import { fileUri, picker } from '@kit.CoreFileKit'; import { VipData } from '../viewmodel/VipData'; import { LocalMusic } from '../view/LocalMusic'; import { DialogHelper } from '@pura/harmony-dialog'; import { common } from '@kit.AbilityKit'; import * as wxOpenSdk from '@tencent/wechat_open_sdk'; import { OnWXResp, WXApi, WXEventHandler } from '../common/util/WXApiWrap'; import { ErrCode } from '@tencent/wechat_open_sdk'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { http } from '@kit.NetworkKit'; import { bundleManager } from '@kit.AbilityKit'; import { cryptoFramework } from '@kit.CryptoArchitectureKit'; import { JSON } from '@kit.ArkTS'; const context = getContext(this) as common.UIAbilityContext; @Preview @Component @Entry export struct VipPage{ @State bundleName:string ='' @State titleBarModel: TitleBar.Model = new TitleBar.Model() .setTitleTextStyle(FontStyle.Normal) .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT) .setLeftIcon($r('app.media.left_back_white')) .setTitleName('赞助中心') .setTitleFontColor(Color.White) .setTitleBarBackground($r('app.color.title_bar_bg')) .setLeftTitleBackground($r('app.color.title_bar_bg')) .setTitleBarBottomLineColor($r('app.color.title_bar_bg')) .setOnLeftClickListener(() => { router.back() }) @State isShowDutySheetView: boolean = false; @State isAgree: boolean = false; static readonly IS_AGREE: string = 'isAgree'; static readonly FOEVEER_DATE: string = 'forever'; @State params: object = router.getParams() @State payPrice:number = 38.8 //9是一个 19是一年 19是永久赞助 static readonly TYPE_MONTH: number = 9.8; static readonly TYPE_YEAR: number = 19 static readonly TYPE_FOREVER: number = 38.8 static readonly VIP_FILEPATH: string = '.vv' @State appName:string = '' @State expireDate:string = '' @State filePath:string = '' @State payType:number = 0 private wxApi = WXApi private wxEventHandler = WXEventHandler @State resultText: string = '' static readonly WX_NOTIFY_URL: string = 'https://webhook.ss5.xyz/wechat_notify.php' // 组件生命周期 async aboutToAppear() { this.isAgree = PreferencesUtil.getBooleanSync(VipPage.IS_AGREE,false) Utility.getAppName(getContext(this)).then((appName:string)=>{ this.appName = appName }) const documentViewPicker = new picker.DocumentViewPicker() let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD }) let download_path = new fileUri.FileUri(documentSaveResult[0]).path + '/' this.filePath = download_path + LocalMusic.STR_LOCK_VIDEO+ '/' + VipPage.VIP_FILEPATH this.expireDate = this.readDataFromFile(this.filePath) this.bundleName = await AppUtil.getBundleName() this.doCheckHao() this.wxEventHandler.registerOnWXRespCallback(this.onWXResp) } private onWXResp: OnWXResp = (resp) => { if (resp.errCode === ErrCode.ERR_OK) { ToastUtil.showToast('支付成功!') let expireDate = this.getExpireDate(this.payPrice) this.saveDataToFile(expireDate,this.filePath,false ) }else { ToastUtil.showToast('支付失败!') } // this.resultText = JSON.stringify(resp ?? {}, null, 2) } // 组件消失生命周期 aboutToDisappear() { console.info(' aboutToDisappear'); this.wxEventHandler.unregisterOnWXRespCallback(this.onWXResp) } build() { Column() { TitleBar({ model: $titleBarModel }) Scroll() { Column() { Column() { Column() { Image($r('app.media.icon')) .visibility(Visibility.Visible) .borderRadius('100%') .clip(true) .width(38) .height(38) Row(){ Text('欢迎尊敬的用户 ') .margin({top:15,bottom:8}) .fontSize(15) .maxLines(1) .fontColor(Color.Gray) .fontWeight(480) Text(this.expireDate===VipPage.FOEVEER_DATE ?'永久赞助':this.expireDate) .margin({top:15,bottom:8}) .fontSize(15) .maxLines(1) .fontColor(Color.Gray) .fontWeight(480) } } .justifyContent(FlexAlign.Center) .width('100%') .height(72) .borderRadius(30) } .height(72) .margin({ left: 15, right: 15, top: 15, bottom: 0 }) Column() { Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Image($r('app.media.vip')) .width(18) .height(18) .alignSelf(ItemAlign.Center) .margin({ left: 22 }) Text(`赞助特权\n1、跳过片头片尾\n2、纯净无广告\n3、更多功能开发中`) .margin({ left: 10, right: 20 }) .fontSize(15) .fontColor(Color.Gray) .fontWeight(480) Blank() } .width('100%') .height(98) .onClick(() => { this.isShowDutySheetView = !this.isShowDutySheetView; }) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Image($r('app.media.icon_person2')) .width(16) .height(16) .alignSelf(ItemAlign.Center) .margin({ left: 22 }) Text(`客服邮箱:${CommonConstants.CONTACT_MAIL}`) .margin({ left: 10, right: 20 }) .fontSize(15) .maxLines(2) .fontColor(Color.Gray) .fontWeight(480) Blank() } .width('100%') .height(45) .onClick(()=>{ ToastUtil.showToast('复制邮件地址成功!') Utility.copyText(CommonConstants.CONTACT_MAIL) }) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Image($r('app.media.selected')) .width(16) .height(16) .alignSelf(ItemAlign.Center) .margin({ left: 22 }) Text('选择赞助时间') .margin({ left: 10, right: 20 }) .fontSize(12) .fontColor(Color.Gray) .fontWeight(480) .id('text_update') Blank() } .width('100%') .height(38) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Text() .width(10) .height(10) .borderRadius('100%') .clip(true) .backgroundColor($r('app.color.title_bar_bg')) .alignSelf(ItemAlign.Center) .margin({ left: 25 }) Text('一个月赞助:'+VipPage.TYPE_MONTH+'元') .margin({ left: 10, right: 20 }) .fontSize(15) .fontColor(Color.Gray) .fontWeight(480) .id('text_update') Blank() Checkbox() .select(this.payPrice===VipPage.TYPE_MONTH?true:false) .selectedColor($r('app.color.title_bar_bg')) .shape(CheckBoxShape.CIRCLE) .onChange((checked: boolean)=>{ if(checked){ this.payPrice = VipPage.TYPE_MONTH } }) .margin({ left: 20, top: 8, bottom: 8,right:20 }) .width(22) .height(22) } .onClick(() => { this.payPrice = VipPage.TYPE_MONTH }) .width('100%') .height(50) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Text() .width(10) .height(10) .borderRadius('100%') .clip(true) .backgroundColor($r('app.color.title_bar_bg')) .alignSelf(ItemAlign.Center) .margin({ left: 25 }) Text('永久赞助:'+VipPage.TYPE_FOREVER+'元 ') .margin({ left: 10, right: 20 }) .fontSize(15) .fontColor(Color.Gray) .fontWeight(480) .id('text_update') Text('(原价¥49.8)') .fontSize(14) .fontColor(Color.Gray) .decoration({ type: TextDecorationType.LineThrough }) Blank() Checkbox() .select(this.payPrice===VipPage.TYPE_FOREVER?true:false) .selectedColor($r('app.color.title_bar_bg')) .onChange((checked: boolean)=>{ if(checked){ this.payPrice = VipPage.TYPE_FOREVER } }) .shape(CheckBoxShape.CIRCLE) .margin({ left: 20, top: 8, bottom: 8,right:20 }) .width(22) .height(22) } .width('100%') .height(50) .onClick(() => { this.payPrice = VipPage.TYPE_FOREVER }) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Image($r('app.media.selected')) .width(16) .height(16) .alignSelf(ItemAlign.Center) .margin({ left: 22 }) Text('选择支付方式') .margin({ left: 10, right: 20 }) .fontSize(12) .fontColor(Color.Gray) .fontWeight(480) .id('text_update') Blank() } .width('100%') .height(38) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Image($r('app.media.wx_logo')) .width(22) .height(22) .alignSelf(ItemAlign.Center) .margin({ left: 25 }) Text('微信支付') .margin({ left: 10, right: 20 }) .fontSize(15) .fontColor(Color.Gray) .fontWeight(480) Blank() Checkbox() .select(this.payType===0?true:false) .selectedColor($r('app.color.title_bar_bg')) .onChange((checked: boolean)=>{ if(checked){ this.payType =0 }else{ this.payType =1 } }) .shape(CheckBoxShape.CIRCLE) .margin({ left: 20, top: 8, bottom: 8,right:20 }) .width(22) .height(22) } .onClick(() => { this.payType =0 }) .width('100%') .height(50) // .visibility(Visibility.None) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) // .visibility(Visibility.None) Row() { Image($r('app.media.alipay')) .width(22) .height(22) .alignSelf(ItemAlign.Center) .margin({ left: 25 }) Text('支付宝') .margin({ left: 10, right: 20 }) .fontSize(15) .fontColor(Color.Gray) .fontWeight(480) .id('text_update') Blank() Checkbox() .select(this.payType===1?true:false) .selectedColor($r('app.color.title_bar_bg')) .onChange((checked: boolean)=>{ if(checked){ this.payType =1 }else{ this.payType =0 } }) .shape(CheckBoxShape.CIRCLE) .margin({ left: 20, top: 8, bottom: 8,right:20 }) .width(22) .height(22) } .onClick(() => { this.payType =1 }) .width('100%') .height(50) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) Row() { Checkbox() .select(this.isAgree) .selectedColor($r('app.color.title_bar_bg')) .shape(CheckBoxShape.CIRCLE) .onChange((checked: boolean)=>{ this.isAgree = checked }) .margin({ left: 20, top: 8, bottom: 8 }) .width(22) .height(22) Text('请同意用户协议') .margin({ left: 10, right: 20 }) .fontSize(15) .fontColor(Color.Gray) .fontWeight(480) .id('text_update') Blank() Image($r('app.media.arrow_right')) .width(22) .height(22) .margin({ left: 20, right: 20 }) .align(Alignment.Center) } .width('100%') .height(45) .bindSheet($$this.isShowDutySheetView, this.DutySheet(), { height:'85%', dragBar: true, showClose: true, backgroundColor:Color.White, title: { title: $r('app.string.persion_duty') } }) .onClick(() => { this.isShowDutySheetView = !this.isShowDutySheetView; }) Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color')) } .margin({top:20,bottom:15}) Row(){ Button('开始赞助') .fontColor(Color.White) .layoutWeight(1) .height(50) .backgroundColor($r('app.color.title_bar_bg')) .stateEffect(true) .margin({left:10,right:25}) .onClick(()=>{ this.doPay() }) } .margin({top:15,bottom:66}) .width('60%') } .layoutWeight(1) } .scrollBar(BarState.Auto) .height('100%') .alignSelf(ItemAlign.Start) .width('100%') } } doPay(){ if(this.isAgree){ if(this.payType ===0){ this.doWxPay() }else{ this.goToAliPay() } }else{ this.isShowDutySheetView = true } } doWxPay = () => { isHasWX().then((result:boolean) =>{ if(!result){ ToastUtil.showToast("没有安装微信") return } //需要德本的服务端支持,德本的接口返回prepayId createWeChatOrder(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID, this.appName, this.payPrice*100,CommonConstants.OUT_TRADE_NO_PRE) .then((prepayId: string) => { this.wechatPay(prepayId) // console.log('onecold Prepay ID:', prepayId); }) .catch((error: Error) => { console.error('onecold Error:', error); }); // createWeChatOrderInfo(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID, // this.appName, this.payPrice*100,getOutTradeNo()) // .then((prepayId: string) => { // this.wechatPay(prepayId) // }) // .catch((error: Error) => { // console.error('onecold Error:', error); // }); }) } //微信支付 async wechatPay(prepayId: string) { console.log('onecold Prepay ID:', prepayId); let nonceStr = RandomUtil.getRandomStr(16,'abcdefghijklmnopqrstuvwxyz') const timestamp: number = Math.floor(Date.now() / 1000); const signString = constructSignatureString(CommonConstants.WX_PAY_APPID,timestamp+'',nonceStr,prepayId) // const signString = `${CommonConstants.WX_PAY_APPID}\n${timestamp}\n${nonceStr}\n${prepayId}\n`; console.log("onecold rsasignRSA ", 'signString: ' + signString); let signStr = await rsaSign2048(signString); let signStrBase64 = Base64Util.encodeToStrSync(signStr) console.log("onecold rsasignRSA ", 'signStr: ' + signStr); console.log("onecold rsasignRSA ", 'signStrBase64: ' + signStrBase64); let req = new wxOpenSdk.PayReq req.partnerId = CommonConstants.WX_PAY_MCHID //剑虾的商户号mchid对应的值。 req.appId =CommonConstants.WX_PAY_APPID //微信开放平台审核通过的移动应用AppID 。 req.packageValue = 'Sign=WXPay' //填写固定值Sign=WXPay req.prepayId = prepayId //调用微信支付下单接口返回的支付交易会话ID,该值有效期为2小时。 req.nonceStr = nonceStr //随机字符串,不长于32位。推荐随机数生成算法。 req.timeStamp = timestamp+'' //时间戳,标准北京时间 req.sign = signStrBase64 req.extData = 'extData' let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req) hilog.info(0x0000,'onecold','send request finished: ', finished) } goToAliPay(){ /* * 支付宝支付:这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险; * * orderInfo 的获取必须来自服务端; */ OrderInfoUtil.getOrderInfo(this.payPrice,this.appName).then((orderInfo) => { new Pay().pay(orderInfo, true).then(async (result) => { let message = `resultStatus: ${result.get('resultStatus')} memo: ${result.get('memo')} result: ${result.get('result')}`; // console.log(message); if(result.get('resultStatus') === '9000'){//9000支付成功,6001取消支付 ToastUtil.showToast('支付成功!') let expireDate = this.getExpireDate(this.payPrice) this.saveDataToFile(expireDate,this.filePath,false ) }else{ ToastUtil.showToast('取消支付!') } }).catch((error: BusinessError) => { console.log(error.message); ToastUtil.showToast('支付失败!'+error.message) }); }); } getExpireDate(payPrice:number){ const now = new Date(); if(payPrice == VipPage.TYPE_MONTH){ // 返回一个月后的时间 now.setMonth(now.getMonth() + 1); }else if(payPrice === VipPage.TYPE_YEAR){ // 返回一年后的时间 now.setFullYear(now.getFullYear() + 1); }else if(payPrice === VipPage.TYPE_FOREVER){ return VipPage.FOEVEER_DATE } return DateUtil.getFormatDateStr(now,'yyyy-MM-dd HH:mm'); } readDataFromFile(filePath:string){ if(!FileUtil.accessSync(filePath)){ return '' } let readTextOptions: ReadTextOptions = { offset: 0, length: 0, encoding: 'utf-8' }; let stat = fileIo.statSync(filePath); readTextOptions.length = stat.size; let str = fileIo.readTextSync(filePath, readTextOptions); const parsedData:VipData = JSON.parse(str) as VipData; // LogUtil.info('onecold parsedData.expireDate = ' + parsedData.expireDate) let result = Base64Util.decodeSync( parsedData.expireDate) // LogUtil.info('onecold 解密的result = ' +StrUtil.unit8ArrayToStr(result)) return StrUtil.unit8ArrayToStr(result); } async saveDataToFile(expireDate:string, filePath:string,isHao:boolean) { let newExpireDate = expireDate if(expireDate!==VipPage.FOEVEER_DATE){ if(FileUtil.accessSync(filePath)&&!isHao){ let readDate = this.readDataFromFile(filePath) let currentDate = new Date(); if(readDate === VipPage.FOEVEER_DATE){ newExpireDate = VipPage.FOEVEER_DATE }else{ //如果里面有值,直接把日期相加 if(DateUtil.getFormatDate(readDate) > currentDate){ let iDate = DateUtil.compareDays(currentDate,readDate) // LogUtil.info('onecold iDate = ' + iDate) let itemDate = DateUtil.getFormatDate(expireDate); newExpireDate =DateUtil.getFormatDateStr(Utility.addDays(itemDate,iDate),'yyyy-MM-dd HH:mm') // LogUtil.info('onecold newExpireDate = ' + newExpireDate) } } } }else{ newExpireDate = VipPage.FOEVEER_DATE } this.expireDate = newExpireDate Utility.setNoble(this.expireDate) //将newExpireDate进行加密 newExpireDate = Base64Util.encodeToStrSync(StrUtil.strToUint8Array(newExpireDate)) // LogUtil.info('onecold 加密后的newExpireDate = ' + newExpireDate) let newItem = new VipData(newExpireDate,'',true) try { let jsonData = JSON.stringify(newItem); const file = await fileIo.open(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE); // 清空文件内容 await fileIo.truncate(file.fd, 0); await fileIo.write(file.fd, jsonData); await fileIo.close(file); console.log('Data saved successfully.'); } catch (error) { console.error('Failed to save data:', error); } } @Builder DutySheet() { Column(){ this.DutyView() } .width('100%') .height('100%') } private webController: WebviewController = new webview.WebviewController(); ports: webview.WebMessagePort[] = []; @State webUrl: string = CommonConstants.NEW_DUTY @State progressValue: number = 0 @Builder DutyView(){ Progress({ value: 0, total: 100, type: ProgressType.Linear }) .backgroundColor(0xFFFFFF) .color(0x414AFF) .value(this.progressValue) .width('%100') .height(2) // .visibility(this.progressIsVisible) .visibility(this.progressValue!=100 ? Visibility.Visible : Visibility.None) Column(){ Web({ src: this.webUrl, controller: this.webController }) .width("100%") .height("100%") .layoutWeight(1) .domStorageAccess(true) .onProgressChange((event)=>{ if (event) { console.log('newProgress:' + event.newProgress); this.progressValue = event.newProgress } }) Row(){ Button('不同意') .fontColor(Color.White) .backgroundColor($r('app.color.title_bar_bg')) .height(50) .layoutWeight(1) .stateEffect(true) .margin({left:25,right:10}) .onClick(()=>{ this.isAgree = false PreferencesUtil.putSync(VipPage.IS_AGREE,this.isAgree) this.isShowDutySheetView = false }) Button('同意') .fontColor(Color.White) .layoutWeight(1) .height(50) .backgroundColor($r('app.color.title_bar_bg')) .stateEffect(true) .margin({left:10,right:25}) .onClick(()=>{ this.isAgree = true PreferencesUtil.putSync(VipPage.IS_AGREE,this.isAgree) this.isShowDutySheetView = false }) } .margin({top:20,bottom:30}) } } doCheckHao(){ if(Utility.isHaoOpenTime()){ let isHao:boolean = PreferencesUtil.getBooleanSync('is_vhaoping'); if(!isHao){ this.showHaopingDialog() return } } } showHaopingDialog(){ setTimeout(()=>{ DialogHelper.showCustomContentDialog({ dialogId:'haoping', title: "好评福利", autoCancel: false, //点击遮障层时,不关闭弹窗 backCancel: true, //点击返回键,不关闭弹窗 contentBuilder: () => { this.customHaoPingBuilder("好消息!好评可以返一个月无广告特权!\n\n如果您觉得好用可以给个好评吗?谢谢!\n\n给个好评吧,非常感谢您的支持!") }, buttons: [], }) },3000) } //好评的自定义内容 @Builder customHaoPingBuilder(content: string) { Column() { Text(content) .fontColor(Color.Gray) .fontSize(16) .alignSelf(ItemAlign.Start) .margin({bottom:15}) .fontSize(16) Row(){ Button('取消') .fontColor(Color.White) .backgroundColor($r('app.color.title_bar_bg')) //.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]}) .height(50) .layoutWeight(1) .stateEffect(true) .margin({right:6}) .onClick(()=>{ DialogHelper.closeDialog('haoping'); //关闭弹框 }) Button('好评') .fontColor(Color.White) //.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]}) .layoutWeight(1) .height(50) .backgroundColor($r('app.color.title_bar_bg')) .stateEffect(true) .margin({left:6}) .onClick(()=>{ DialogHelper.closeDialog('haoping'); //关闭弹框 Utility.gotoMarket(context,this.bundleName) PreferencesUtil.putSync("is_vhaoping", true); setTimeout(()=>{ let expireDate = this.getExpireDate(VipPage.TYPE_MONTH) this.saveDataToFile(expireDate,this.filePath,true ) },360000) }) } } .width("100%") .padding(10) } } // 定义响应数据的类型 interface WeChatOrderResponse { prepay_id: string; } interface Amount { total: number; currency: string; } //调用服务端接口,用户在商户APP端选择微信支付后,商户需调用该接口在微信支付下单,生成用于调起支付的预支付交易会话标识(prepay_id)。 async function createWeChatOrder( app_id: string, mch_id: string, description: string, amount: number, out_trade_no_pre: 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) + '&out_trade_no_pre=' + encodeURIComponent(out_trade_no_pre) LogUtil.debug("onecold 微信支付 requestUrl=" + requestUrl) 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: WeChatOrderResponse; LogUtil.debug("onecold 微信支付 response.result=" + response.result) if (typeof response.result === 'string') { responseData = JSON.parse(response.result) as WeChatOrderResponse; } else if (response.result instanceof Object) { responseData = response.result as WeChatOrderResponse; } else { throw new Error('onecold Unexpected response format'); } const prepayId: string = responseData.prepay_id; console.log('onecold WeChat Order Created, prepay_id:', prepayId); return prepayId; } else { console.error('Failed to create WeChat order:', response.responseCode, response.result); throw new Error(`HTTP Error: ${response.responseCode}`); } } catch (error) { console.error('Error creating WeChat order:', error); return ''; } } async function isHasWX(): Promise{ try { let link = 'weixin://'; let data = bundleManager.canOpenLink(link); if (data) { return true }else { promptAction.showToast({message:'微信未安装'}) return false } } catch (err) { let message = (err as BusinessError).message; console.error('canOpenLink failed: %{public}s', message); return false } } // 微信支付构造签名串 function constructSignatureString(appId: string, timestamp: string, nonceStr: string, prepayId: string): string { return `${appId}\n${timestamp}\n${nonceStr}\n${prepayId}\n`; } // 微信支付RSA2048|PKCS1|SHA256 APP调起支付签名 async function 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() } } // 定义接口请求参数类型 interface WeChatOrderRequest { appid: string; mchid: string; description: string; out_trade_no: string; notify_url: string; amount: Amount; } function generateSignatureInfo(timestamp: string,nonceStr: string,body:string): string { const httpMethod = 'POST'; const url = '/v3/pay/transactions/jsapi'; // const timestamp = Math.floor(Date.now() / 1000).toString(); // const body = '{"appid":"wxd678efh567hg6787","mchid":"1900007291","description":"Image形象店-深圳腾大-QQ公仔","out_trade_no":"1217752501201407033233368018","notify_url":"https://www.weixin.qq.com/wxpay/pay.php","amount":{"total":100,"currency":"CNY"},"payer":{"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}}'; console.log("onecold rsasignRSA ", 'body: ' + body); const signatureString = `${httpMethod}\n${url}\n${timestamp}\n${nonceStr}\n${body}\n`; return signatureString } function getOutTradeNo(): string { const timestamp = Date.now().toString(); // 获取当前时间戳 const randomPart = Math.floor(Math.random() * 1e16).toString().padStart(16, '0'); // 生成16位随机数 const orderNumber = (timestamp + randomPart).slice(0, 32); // 确保长度为32位 return orderNumber; } // 构造Authorization头 function constructAuthorizationHeader(mchid: string,nonceStr:string,signature: string, timestamp:string,serialNo:string): string { return `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${serialNo}"`; } async function createWeChatOrderInfo(appid: string, mchid: string, description: string,amount: number, outTradeNo: string): Promise { const url: string = 'https://api.mch.weixin.qq.com/v3/pay/transactions/app'; let nonceStr = RandomUtil.getRandomStr(16,'abcdefghijklmnopqrstuvwxyz') const timestamp: number = Math.floor(Date.now() / 1000); // 构建请求体 const body: WeChatOrderRequest = { appid: appid, mchid: mchid, description: description, out_trade_no: outTradeNo, notify_url: VipPage.WX_NOTIFY_URL, amount: { total: amount, currency: 'CNY'} }; let generateSign:string= generateSignatureInfo(timestamp+'',nonceStr, JSON.stringify(body)) console.log("onecold rsasignRSA ", 'generateSign: ' + generateSign); let signStr = await rsaSign2048(generateSign); let signStrBase64 = Base64Util.encodeToStrSync(signStr) let authorizationHeader = constructAuthorizationHeader(mchid,nonceStr,signStrBase64,timestamp+'',CommonConstants.WX_PAY_SERIALNO) console.log("onecold rsasignRSA ", 'signStr: ' + signStr); console.log("onecold rsasignRSA ", 'signStrBase64: ' + signStrBase64); console.log("onecold rsasignRSA ", 'authorizationHeader: ' + authorizationHeader); // 构建请求头 const headers: Record = { 'Authorization': authorizationHeader, // 替换为你的授权信息 'Accept': 'application/json', 'Content-Type': 'application/json', }; const httpRequest: http.HttpRequest = http.createHttp(); const options: http.HttpRequestOptions = { method: http.RequestMethod.POST, header: headers, extraData: JSON.stringify(body), readTimeout: 6000, connectTimeout: 6000, }; try { const response: http.HttpResponse = await httpRequest.request(url, options); LogUtil.debug("onecold 微信支付 response.result=" + response.result) if (response.responseCode === 200) { // LogUtil.debug("onecold 微信支付 response.result=" + response.result) let responseData: WeChatOrderResponse; if (typeof response.result === 'string') { responseData = JSON.parse(response.result) as WeChatOrderResponse; } else if (response.result instanceof Object) { responseData = response.result as WeChatOrderResponse; } else { throw new Error('Unexpected response format'); } const prepayId: string = responseData.prepay_id; console.log('WeChat Order Created, prepay_id:', prepayId); return prepayId; } else { console.error('Failed to create WeChat order:', response.responseCode, response.result); throw new Error(`HTTP Error: ${response.responseCode}`); } } catch (error) { console.error('Error creating WeChat order:', error); return ''; } }