VipPage.ets 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  1. import { promptAction, router, window } from '@kit.ArkUI';
  2. import TitleBar from '../view/TitleBar';
  3. import { webview } from '@kit.ArkWeb';
  4. import { CommonConstants } from '../common/constants/CommonConstants';
  5. import { AES,
  6. AppUtil,
  7. Base64Util,
  8. CryptoHelper, DateUtil, FileUtil, LogUtil, PreferencesUtil,
  9. RandomUtil,
  10. StrUtil,
  11. ToastUtil } from '@pura/harmony-utils';
  12. import { BusinessError } from '@kit.BasicServicesKit';
  13. import { Pay } from '@cashier_alipay/cashiersdk';
  14. import { OrderInfoUtil } from '../alipay/OrderInfoUtil';
  15. import { Utility } from '../common/util/Utility';
  16. import fileIo, { ReadTextOptions } from '@ohos.file.fs';
  17. import { fileUri, picker } from '@kit.CoreFileKit';
  18. import { VipData } from '../viewmodel/VipData';
  19. import { LocalMusic } from '../view/LocalMusic';
  20. import { DialogHelper } from '@pura/harmony-dialog';
  21. import { common } from '@kit.AbilityKit';
  22. import * as wxOpenSdk from '@tencent/wechat_open_sdk';
  23. import { OnWXResp, WXApi, WXEventHandler } from '../common/util/WXApiWrap';
  24. import { ErrCode } from '@tencent/wechat_open_sdk';
  25. import { hilog } from '@kit.PerformanceAnalysisKit';
  26. import { http } from '@kit.NetworkKit';
  27. import { bundleManager } from '@kit.AbilityKit';
  28. import { cryptoFramework } from '@kit.CryptoArchitectureKit';
  29. import { JSON } from '@kit.ArkTS';
  30. const context = getContext(this) as common.UIAbilityContext;
  31. @Preview
  32. @Component
  33. @Entry
  34. export struct VipPage{
  35. @State bundleName:string =''
  36. @State titleBarModel: TitleBar.Model = new TitleBar.Model()
  37. .setTitleTextStyle(FontStyle.Normal)
  38. .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT)
  39. .setLeftIcon($r('app.media.left_back_white'))
  40. .setTitleName('赞助中心')
  41. .setTitleFontColor(Color.White)
  42. .setTitleBarBackground($r('app.color.title_bar_bg'))
  43. .setLeftTitleBackground($r('app.color.title_bar_bg'))
  44. .setTitleBarBottomLineColor($r('app.color.title_bar_bg'))
  45. .setOnLeftClickListener(() => {
  46. router.back()
  47. })
  48. @State isShowDutySheetView: boolean = false;
  49. @State isAgree: boolean = false;
  50. static readonly IS_AGREE: string = 'isAgree';
  51. static readonly FOEVEER_DATE: string = 'forever';
  52. @State params: object = router.getParams()
  53. @State payPrice:number = 38.8 //9是一个 19是一年 19是永久赞助
  54. static readonly TYPE_MONTH: number = 9.8;
  55. static readonly TYPE_YEAR: number = 19
  56. static readonly TYPE_FOREVER: number = 38.8
  57. static readonly VIP_FILEPATH: string = '.vv'
  58. @State appName:string = ''
  59. @State expireDate:string = ''
  60. @State filePath:string = ''
  61. @State payType:number = 0
  62. private wxApi = WXApi
  63. private wxEventHandler = WXEventHandler
  64. @State resultText: string = ''
  65. static readonly WX_NOTIFY_URL: string = 'https://webhook.ss5.xyz/wechat_notify.php'
  66. // 组件生命周期
  67. async aboutToAppear() {
  68. this.isAgree = PreferencesUtil.getBooleanSync(VipPage.IS_AGREE,false)
  69. Utility.getAppName(getContext(this)).then((appName:string)=>{
  70. this.appName = appName
  71. })
  72. const documentViewPicker = new picker.DocumentViewPicker()
  73. let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
  74. let download_path = new fileUri.FileUri(documentSaveResult[0]).path + '/'
  75. this.filePath = download_path + LocalMusic.STR_LOCK_VIDEO+ '/' + VipPage.VIP_FILEPATH
  76. this.expireDate = this.readDataFromFile(this.filePath)
  77. this.bundleName = await AppUtil.getBundleName()
  78. this.doCheckHao()
  79. this.wxEventHandler.registerOnWXRespCallback(this.onWXResp)
  80. }
  81. private onWXResp: OnWXResp = (resp) => {
  82. if (resp.errCode === ErrCode.ERR_OK) {
  83. ToastUtil.showToast('支付成功!')
  84. let expireDate = this.getExpireDate(this.payPrice)
  85. this.saveDataToFile(expireDate,this.filePath,false )
  86. }else {
  87. ToastUtil.showToast('支付失败!')
  88. }
  89. // this.resultText = JSON.stringify(resp ?? {}, null, 2)
  90. }
  91. // 组件消失生命周期
  92. aboutToDisappear() {
  93. console.info(' aboutToDisappear');
  94. this.wxEventHandler.unregisterOnWXRespCallback(this.onWXResp)
  95. }
  96. build() {
  97. Column() {
  98. TitleBar({ model: $titleBarModel })
  99. Scroll() {
  100. Column() {
  101. Column() {
  102. Column() {
  103. Image($r('app.media.icon'))
  104. .visibility(Visibility.Visible)
  105. .borderRadius('100%')
  106. .clip(true)
  107. .width(38)
  108. .height(38)
  109. Row(){
  110. Text('欢迎尊敬的用户 ')
  111. .margin({top:15,bottom:8})
  112. .fontSize(15)
  113. .maxLines(1)
  114. .fontColor(Color.Gray)
  115. .fontWeight(480)
  116. Text(this.expireDate===VipPage.FOEVEER_DATE ?'永久赞助':this.expireDate)
  117. .margin({top:15,bottom:8})
  118. .fontSize(15)
  119. .maxLines(1)
  120. .fontColor(Color.Gray)
  121. .fontWeight(480)
  122. }
  123. }
  124. .justifyContent(FlexAlign.Center)
  125. .width('100%')
  126. .height(72)
  127. .borderRadius(30)
  128. }
  129. .height(72)
  130. .margin({
  131. left: 15,
  132. right: 15,
  133. top: 15,
  134. bottom: 0
  135. })
  136. Column() {
  137. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  138. Row() {
  139. Image($r('app.media.vip'))
  140. .width(18)
  141. .height(18)
  142. .alignSelf(ItemAlign.Center)
  143. .margin({ left: 22 })
  144. Text(`赞助特权\n1、跳过片头片尾\n2、纯净无广告\n3、更多功能开发中`)
  145. .margin({ left: 10, right: 20 })
  146. .fontSize(15)
  147. .fontColor(Color.Gray)
  148. .fontWeight(480)
  149. Blank()
  150. }
  151. .width('100%')
  152. .height(98)
  153. .onClick(() => {
  154. this.isShowDutySheetView = !this.isShowDutySheetView;
  155. })
  156. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  157. Row() {
  158. Image($r('app.media.icon_person2'))
  159. .width(16)
  160. .height(16)
  161. .alignSelf(ItemAlign.Center)
  162. .margin({ left: 22 })
  163. Text(`客服邮箱:${CommonConstants.CONTACT_MAIL}`)
  164. .margin({ left: 10, right: 20 })
  165. .fontSize(15)
  166. .maxLines(2)
  167. .fontColor(Color.Gray)
  168. .fontWeight(480)
  169. Blank()
  170. }
  171. .width('100%')
  172. .height(45)
  173. .onClick(()=>{
  174. ToastUtil.showToast('复制邮件地址成功!')
  175. Utility.copyText(CommonConstants.CONTACT_MAIL)
  176. })
  177. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  178. Row() {
  179. Image($r('app.media.selected'))
  180. .width(16)
  181. .height(16)
  182. .alignSelf(ItemAlign.Center)
  183. .margin({ left: 22 })
  184. Text('选择赞助时间')
  185. .margin({ left: 10, right: 20 })
  186. .fontSize(12)
  187. .fontColor(Color.Gray)
  188. .fontWeight(480)
  189. .id('text_update')
  190. Blank()
  191. }
  192. .width('100%')
  193. .height(38)
  194. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  195. Row() {
  196. Text()
  197. .width(10)
  198. .height(10)
  199. .borderRadius('100%')
  200. .clip(true)
  201. .backgroundColor($r('app.color.title_bar_bg'))
  202. .alignSelf(ItemAlign.Center)
  203. .margin({ left: 25 })
  204. Text('一个月赞助:'+VipPage.TYPE_MONTH+'元')
  205. .margin({ left: 10, right: 20 })
  206. .fontSize(15)
  207. .fontColor(Color.Gray)
  208. .fontWeight(480)
  209. .id('text_update')
  210. Blank()
  211. Checkbox()
  212. .select(this.payPrice===VipPage.TYPE_MONTH?true:false)
  213. .selectedColor($r('app.color.title_bar_bg'))
  214. .shape(CheckBoxShape.CIRCLE)
  215. .onChange((checked: boolean)=>{
  216. if(checked){
  217. this.payPrice = VipPage.TYPE_MONTH
  218. }
  219. })
  220. .margin({ left: 20, top: 8, bottom: 8,right:20 })
  221. .width(22)
  222. .height(22)
  223. }
  224. .onClick(() => {
  225. this.payPrice = VipPage.TYPE_MONTH
  226. })
  227. .width('100%')
  228. .height(50)
  229. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  230. Row() {
  231. Text()
  232. .width(10)
  233. .height(10)
  234. .borderRadius('100%')
  235. .clip(true)
  236. .backgroundColor($r('app.color.title_bar_bg'))
  237. .alignSelf(ItemAlign.Center)
  238. .margin({ left: 25 })
  239. Text('永久赞助:'+VipPage.TYPE_FOREVER+'元 ')
  240. .margin({ left: 10, right: 20 })
  241. .fontSize(15)
  242. .fontColor(Color.Gray)
  243. .fontWeight(480)
  244. .id('text_update')
  245. Text('(原价¥49.8)')
  246. .fontSize(14)
  247. .fontColor(Color.Gray)
  248. .decoration({ type: TextDecorationType.LineThrough })
  249. Blank()
  250. Checkbox()
  251. .select(this.payPrice===VipPage.TYPE_FOREVER?true:false)
  252. .selectedColor($r('app.color.title_bar_bg'))
  253. .onChange((checked: boolean)=>{
  254. if(checked){
  255. this.payPrice = VipPage.TYPE_FOREVER
  256. }
  257. })
  258. .shape(CheckBoxShape.CIRCLE)
  259. .margin({ left: 20, top: 8, bottom: 8,right:20 })
  260. .width(22)
  261. .height(22)
  262. }
  263. .width('100%')
  264. .height(50)
  265. .onClick(() => {
  266. this.payPrice = VipPage.TYPE_FOREVER
  267. })
  268. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  269. Row() {
  270. Image($r('app.media.selected'))
  271. .width(16)
  272. .height(16)
  273. .alignSelf(ItemAlign.Center)
  274. .margin({ left: 22 })
  275. Text('选择支付方式')
  276. .margin({ left: 10, right: 20 })
  277. .fontSize(12)
  278. .fontColor(Color.Gray)
  279. .fontWeight(480)
  280. .id('text_update')
  281. Blank()
  282. }
  283. .width('100%')
  284. .height(38)
  285. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  286. Row() {
  287. Image($r('app.media.wx_logo'))
  288. .width(22)
  289. .height(22)
  290. .alignSelf(ItemAlign.Center)
  291. .margin({ left: 25 })
  292. Text('微信支付')
  293. .margin({ left: 10, right: 20 })
  294. .fontSize(15)
  295. .fontColor(Color.Gray)
  296. .fontWeight(480)
  297. Blank()
  298. Checkbox()
  299. .select(this.payType===0?true:false)
  300. .selectedColor($r('app.color.title_bar_bg'))
  301. .onChange((checked: boolean)=>{
  302. if(checked){
  303. this.payType =0
  304. }else{
  305. this.payType =1
  306. }
  307. })
  308. .shape(CheckBoxShape.CIRCLE)
  309. .margin({ left: 20, top: 8, bottom: 8,right:20 })
  310. .width(22)
  311. .height(22)
  312. }
  313. .onClick(() => {
  314. this.payType =0
  315. })
  316. .width('100%')
  317. .height(50)
  318. // .visibility(Visibility.None)
  319. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  320. // .visibility(Visibility.None)
  321. Row() {
  322. Image($r('app.media.alipay'))
  323. .width(22)
  324. .height(22)
  325. .alignSelf(ItemAlign.Center)
  326. .margin({ left: 25 })
  327. Text('支付宝')
  328. .margin({ left: 10, right: 20 })
  329. .fontSize(15)
  330. .fontColor(Color.Gray)
  331. .fontWeight(480)
  332. .id('text_update')
  333. Blank()
  334. Checkbox()
  335. .select(this.payType===1?true:false)
  336. .selectedColor($r('app.color.title_bar_bg'))
  337. .onChange((checked: boolean)=>{
  338. if(checked){
  339. this.payType =1
  340. }else{
  341. this.payType =0
  342. }
  343. })
  344. .shape(CheckBoxShape.CIRCLE)
  345. .margin({ left: 20, top: 8, bottom: 8,right:20 })
  346. .width(22)
  347. .height(22)
  348. }
  349. .onClick(() => {
  350. this.payType =1
  351. })
  352. .width('100%')
  353. .height(50)
  354. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  355. Row() {
  356. Checkbox()
  357. .select(this.isAgree)
  358. .selectedColor($r('app.color.title_bar_bg'))
  359. .shape(CheckBoxShape.CIRCLE)
  360. .onChange((checked: boolean)=>{
  361. this.isAgree = checked
  362. })
  363. .margin({ left: 20, top: 8, bottom: 8 })
  364. .width(22)
  365. .height(22)
  366. Text('请同意用户协议')
  367. .margin({ left: 10, right: 20 })
  368. .fontSize(15)
  369. .fontColor(Color.Gray)
  370. .fontWeight(480)
  371. .id('text_update')
  372. Blank()
  373. Image($r('app.media.arrow_right'))
  374. .width(22)
  375. .height(22)
  376. .margin({ left: 20, right: 20 })
  377. .align(Alignment.Center)
  378. }
  379. .width('100%')
  380. .height(45)
  381. .bindSheet($$this.isShowDutySheetView, this.DutySheet(), {
  382. height:'85%',
  383. dragBar: true,
  384. showClose: true,
  385. backgroundColor:Color.White,
  386. title: { title: $r('app.string.persion_duty') }
  387. })
  388. .onClick(() => {
  389. this.isShowDutySheetView = !this.isShowDutySheetView;
  390. })
  391. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  392. }
  393. .margin({top:20,bottom:15})
  394. Row(){
  395. Button('开始赞助')
  396. .fontColor(Color.White)
  397. .layoutWeight(1)
  398. .height(50)
  399. .backgroundColor($r('app.color.title_bar_bg'))
  400. .stateEffect(true)
  401. .margin({left:10,right:25})
  402. .onClick(()=>{
  403. this.doPay()
  404. })
  405. }
  406. .margin({top:15,bottom:66})
  407. .width('60%')
  408. }
  409. .layoutWeight(1)
  410. }
  411. .scrollBar(BarState.Auto)
  412. .height('100%')
  413. .alignSelf(ItemAlign.Start)
  414. .width('100%')
  415. }
  416. }
  417. doPay(){
  418. if(this.isAgree){
  419. if(this.payType ===0){
  420. this.doWxPay()
  421. }else{
  422. this.goToAliPay()
  423. }
  424. }else{
  425. this.isShowDutySheetView = true
  426. }
  427. }
  428. doWxPay = () => {
  429. isHasWX().then((result:boolean) =>{
  430. if(!result){
  431. ToastUtil.showToast("没有安装微信")
  432. return
  433. }
  434. //需要德本的服务端支持,德本的接口返回prepayId
  435. createWeChatOrder(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID,
  436. this.appName, this.payPrice*100,CommonConstants.OUT_TRADE_NO_PRE)
  437. .then((prepayId: string) => {
  438. this.wechatPay(prepayId)
  439. // console.log('onecold Prepay ID:', prepayId);
  440. })
  441. .catch((error: Error) => {
  442. console.error('onecold Error:', error);
  443. });
  444. // createWeChatOrderInfo(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID,
  445. // this.appName, this.payPrice*100,getOutTradeNo())
  446. // .then((prepayId: string) => {
  447. // this.wechatPay(prepayId)
  448. // })
  449. // .catch((error: Error) => {
  450. // console.error('onecold Error:', error);
  451. // });
  452. })
  453. }
  454. //微信支付
  455. async wechatPay(prepayId: string) {
  456. console.log('onecold Prepay ID:', prepayId);
  457. let nonceStr = RandomUtil.getRandomStr(16,'abcdefghijklmnopqrstuvwxyz')
  458. const timestamp: number = Math.floor(Date.now() / 1000);
  459. const signString = constructSignatureString(CommonConstants.WX_PAY_APPID,timestamp+'',nonceStr,prepayId)
  460. // const signString = `${CommonConstants.WX_PAY_APPID}\n${timestamp}\n${nonceStr}\n${prepayId}\n`;
  461. console.log("onecold rsasignRSA ", 'signString: ' + signString);
  462. let signStr = await rsaSign2048(signString);
  463. let signStrBase64 = Base64Util.encodeToStrSync(signStr)
  464. console.log("onecold rsasignRSA ", 'signStr: ' + signStr);
  465. console.log("onecold rsasignRSA ", 'signStrBase64: ' + signStrBase64);
  466. let req = new wxOpenSdk.PayReq
  467. req.partnerId = CommonConstants.WX_PAY_MCHID //剑虾的商户号mchid对应的值。
  468. req.appId =CommonConstants.WX_PAY_APPID //微信开放平台审核通过的移动应用AppID 。
  469. req.packageValue = 'Sign=WXPay' //填写固定值Sign=WXPay
  470. req.prepayId = prepayId //调用微信支付下单接口返回的支付交易会话ID,该值有效期为2小时。
  471. req.nonceStr = nonceStr //随机字符串,不长于32位。推荐随机数生成算法。
  472. req.timeStamp = timestamp+'' //时间戳,标准北京时间
  473. req.sign = signStrBase64
  474. req.extData = 'extData'
  475. let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req)
  476. hilog.info(0x0000,'onecold','send request finished: ', finished)
  477. }
  478. goToAliPay(){
  479. /*
  480. * 支付宝支付:这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;
  481. * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;
  482. * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;
  483. *
  484. * orderInfo 的获取必须来自服务端;
  485. */
  486. OrderInfoUtil.getOrderInfo(this.payPrice,this.appName).then((orderInfo) => {
  487. new Pay().pay(orderInfo, true).then(async (result) => {
  488. let message =
  489. `resultStatus: ${result.get('resultStatus')} memo: ${result.get('memo')} result: ${result.get('result')}`;
  490. // console.log(message);
  491. if(result.get('resultStatus') === '9000'){//9000支付成功,6001取消支付
  492. ToastUtil.showToast('支付成功!')
  493. let expireDate = this.getExpireDate(this.payPrice)
  494. this.saveDataToFile(expireDate,this.filePath,false )
  495. }else{
  496. ToastUtil.showToast('取消支付!')
  497. }
  498. }).catch((error: BusinessError) => {
  499. console.log(error.message);
  500. ToastUtil.showToast('支付失败!'+error.message)
  501. });
  502. });
  503. }
  504. getExpireDate(payPrice:number){
  505. const now = new Date();
  506. if(payPrice == VipPage.TYPE_MONTH){
  507. // 返回一个月后的时间
  508. now.setMonth(now.getMonth() + 1);
  509. }else if(payPrice === VipPage.TYPE_YEAR){
  510. // 返回一年后的时间
  511. now.setFullYear(now.getFullYear() + 1);
  512. }else if(payPrice === VipPage.TYPE_FOREVER){
  513. return VipPage.FOEVEER_DATE
  514. }
  515. return DateUtil.getFormatDateStr(now,'yyyy-MM-dd HH:mm');
  516. }
  517. readDataFromFile(filePath:string){
  518. if(!FileUtil.accessSync(filePath)){
  519. return ''
  520. }
  521. let readTextOptions: ReadTextOptions = {
  522. offset: 0,
  523. length: 0,
  524. encoding: 'utf-8'
  525. };
  526. let stat = fileIo.statSync(filePath);
  527. readTextOptions.length = stat.size;
  528. let str = fileIo.readTextSync(filePath, readTextOptions);
  529. const parsedData:VipData = JSON.parse(str) as VipData;
  530. // LogUtil.info('onecold parsedData.expireDate = ' + parsedData.expireDate)
  531. let result = Base64Util.decodeSync( parsedData.expireDate)
  532. // LogUtil.info('onecold 解密的result = ' +StrUtil.unit8ArrayToStr(result))
  533. return StrUtil.unit8ArrayToStr(result);
  534. }
  535. async saveDataToFile(expireDate:string, filePath:string,isHao:boolean) {
  536. let newExpireDate = expireDate
  537. if(expireDate!==VipPage.FOEVEER_DATE){
  538. if(FileUtil.accessSync(filePath)&&!isHao){
  539. let readDate = this.readDataFromFile(filePath)
  540. let currentDate = new Date();
  541. if(readDate === VipPage.FOEVEER_DATE){
  542. newExpireDate = VipPage.FOEVEER_DATE
  543. }else{
  544. //如果里面有值,直接把日期相加
  545. if(DateUtil.getFormatDate(readDate) > currentDate){
  546. let iDate = DateUtil.compareDays(currentDate,readDate)
  547. // LogUtil.info('onecold iDate = ' + iDate)
  548. let itemDate = DateUtil.getFormatDate(expireDate);
  549. newExpireDate =DateUtil.getFormatDateStr(Utility.addDays(itemDate,iDate),'yyyy-MM-dd HH:mm')
  550. // LogUtil.info('onecold newExpireDate = ' + newExpireDate)
  551. }
  552. }
  553. }
  554. }else{
  555. newExpireDate = VipPage.FOEVEER_DATE
  556. }
  557. this.expireDate = newExpireDate
  558. Utility.setNoble(this.expireDate)
  559. //将newExpireDate进行加密
  560. newExpireDate = Base64Util.encodeToStrSync(StrUtil.strToUint8Array(newExpireDate))
  561. // LogUtil.info('onecold 加密后的newExpireDate = ' + newExpireDate)
  562. let newItem = new VipData(newExpireDate,'',true)
  563. try {
  564. let jsonData = JSON.stringify(newItem);
  565. const file = await fileIo.open(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
  566. // 清空文件内容
  567. await fileIo.truncate(file.fd, 0);
  568. await fileIo.write(file.fd, jsonData);
  569. await fileIo.close(file);
  570. console.log('Data saved successfully.');
  571. } catch (error) {
  572. console.error('Failed to save data:', error);
  573. }
  574. }
  575. @Builder
  576. DutySheet() {
  577. Column(){
  578. this.DutyView()
  579. }
  580. .width('100%')
  581. .height('100%')
  582. }
  583. private webController: WebviewController = new webview.WebviewController();
  584. ports: webview.WebMessagePort[] = [];
  585. @State webUrl: string = CommonConstants.NEW_DUTY
  586. @State
  587. progressValue: number = 0
  588. @Builder
  589. DutyView(){
  590. Progress({ value: 0, total: 100, type: ProgressType.Linear })
  591. .backgroundColor(0xFFFFFF)
  592. .color(0x414AFF)
  593. .value(this.progressValue)
  594. .width('%100')
  595. .height(2)
  596. // .visibility(this.progressIsVisible)
  597. .visibility(this.progressValue!=100 ? Visibility.Visible : Visibility.None)
  598. Column(){
  599. Web({
  600. src: this.webUrl,
  601. controller: this.webController
  602. })
  603. .width("100%")
  604. .height("100%")
  605. .layoutWeight(1)
  606. .domStorageAccess(true)
  607. .onProgressChange((event)=>{
  608. if (event) {
  609. console.log('newProgress:' + event.newProgress);
  610. this.progressValue = event.newProgress
  611. }
  612. })
  613. Row(){
  614. Button('不同意')
  615. .fontColor(Color.White)
  616. .backgroundColor($r('app.color.title_bar_bg'))
  617. .height(50)
  618. .layoutWeight(1)
  619. .stateEffect(true)
  620. .margin({left:25,right:10})
  621. .onClick(()=>{
  622. this.isAgree = false
  623. PreferencesUtil.putSync(VipPage.IS_AGREE,this.isAgree)
  624. this.isShowDutySheetView = false
  625. })
  626. Button('同意')
  627. .fontColor(Color.White)
  628. .layoutWeight(1)
  629. .height(50)
  630. .backgroundColor($r('app.color.title_bar_bg'))
  631. .stateEffect(true)
  632. .margin({left:10,right:25})
  633. .onClick(()=>{
  634. this.isAgree = true
  635. PreferencesUtil.putSync(VipPage.IS_AGREE,this.isAgree)
  636. this.isShowDutySheetView = false
  637. })
  638. }
  639. .margin({top:20,bottom:30})
  640. }
  641. }
  642. doCheckHao(){
  643. if(Utility.isHaoOpenTime()){
  644. let isHao:boolean = PreferencesUtil.getBooleanSync('is_vhaoping');
  645. if(!isHao){
  646. this.showHaopingDialog()
  647. return
  648. }
  649. }
  650. }
  651. showHaopingDialog(){
  652. setTimeout(()=>{
  653. DialogHelper.showCustomContentDialog({
  654. dialogId:'haoping',
  655. title: "好评福利",
  656. autoCancel: false, //点击遮障层时,不关闭弹窗
  657. backCancel: true, //点击返回键,不关闭弹窗
  658. contentBuilder: () => {
  659. this.customHaoPingBuilder("好消息!好评可以返一个月无广告特权!\n\n如果您觉得好用可以给个好评吗?谢谢!\n\n给个好评吧,非常感谢您的支持!")
  660. },
  661. buttons: [],
  662. })
  663. },3000)
  664. }
  665. //好评的自定义内容
  666. @Builder
  667. customHaoPingBuilder(content: string) {
  668. Column() {
  669. Text(content)
  670. .fontColor(Color.Gray)
  671. .fontSize(16)
  672. .alignSelf(ItemAlign.Start)
  673. .margin({bottom:15})
  674. .fontSize(16)
  675. Row(){
  676. Button('取消')
  677. .fontColor(Color.White)
  678. .backgroundColor($r('app.color.title_bar_bg'))
  679. //.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]})
  680. .height(50)
  681. .layoutWeight(1)
  682. .stateEffect(true)
  683. .margin({right:6})
  684. .onClick(()=>{
  685. DialogHelper.closeDialog('haoping'); //关闭弹框
  686. })
  687. Button('好评')
  688. .fontColor(Color.White)
  689. //.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]})
  690. .layoutWeight(1)
  691. .height(50)
  692. .backgroundColor($r('app.color.title_bar_bg'))
  693. .stateEffect(true)
  694. .margin({left:6})
  695. .onClick(()=>{
  696. DialogHelper.closeDialog('haoping'); //关闭弹框
  697. Utility.gotoMarket(context,this.bundleName)
  698. PreferencesUtil.putSync("is_vhaoping", true);
  699. setTimeout(()=>{
  700. let expireDate = this.getExpireDate(VipPage.TYPE_MONTH)
  701. this.saveDataToFile(expireDate,this.filePath,true )
  702. },360000)
  703. })
  704. }
  705. }
  706. .width("100%")
  707. .padding(10)
  708. }
  709. }
  710. // 定义响应数据的类型
  711. interface WeChatOrderResponse {
  712. prepay_id: string;
  713. }
  714. interface Amount {
  715. total: number;
  716. currency: string;
  717. }
  718. //调用服务端接口,用户在商户APP端选择微信支付后,商户需调用该接口在微信支付下单,生成用于调起支付的预支付交易会话标识(prepay_id)。
  719. async function createWeChatOrder(
  720. app_id: string,
  721. mch_id: string,
  722. description: string,
  723. amount: number,
  724. out_trade_no_pre: string,
  725. ): Promise<string> {
  726. let requestUrl = CommonConstants.WX_PAY_API
  727. + '?app_id=' + encodeURIComponent(app_id.trim())
  728. + '&mch_id=' + encodeURIComponent(mch_id.trim())
  729. + '&description=' + encodeURIComponent(description.trim())
  730. + '&amount=' + encodeURIComponent(amount)
  731. + '&out_trade_no_pre=' + encodeURIComponent(out_trade_no_pre)
  732. LogUtil.debug("onecold 微信支付 requestUrl=" + requestUrl)
  733. const httpRequest: http.HttpRequest = http.createHttp();
  734. const options: http.HttpRequestOptions = {
  735. method: http.RequestMethod.GET,
  736. readTimeout: 6000,
  737. connectTimeout: 6000,
  738. };
  739. try {
  740. const response: http.HttpResponse = await httpRequest.request(requestUrl, options);
  741. if (response.responseCode === 200) {
  742. let responseData: WeChatOrderResponse;
  743. LogUtil.debug("onecold 微信支付 response.result=" + response.result)
  744. if (typeof response.result === 'string') {
  745. responseData = JSON.parse(response.result) as WeChatOrderResponse;
  746. } else if (response.result instanceof Object) {
  747. responseData = response.result as WeChatOrderResponse;
  748. } else {
  749. throw new Error('onecold Unexpected response format');
  750. }
  751. const prepayId: string = responseData.prepay_id;
  752. console.log('onecold WeChat Order Created, prepay_id:', prepayId);
  753. return prepayId;
  754. } else {
  755. console.error('Failed to create WeChat order:', response.responseCode, response.result);
  756. throw new Error(`HTTP Error: ${response.responseCode}`);
  757. }
  758. } catch (error) {
  759. console.error('Error creating WeChat order:', error);
  760. return '';
  761. }
  762. }
  763. async function isHasWX(): Promise<boolean>{
  764. try {
  765. let link = 'weixin://';
  766. let data = bundleManager.canOpenLink(link);
  767. if (data) {
  768. return true
  769. }else {
  770. promptAction.showToast({message:'微信未安装'})
  771. return false
  772. }
  773. } catch (err) {
  774. let message = (err as BusinessError).message;
  775. console.error('canOpenLink failed: %{public}s', message);
  776. return false
  777. }
  778. }
  779. // 微信支付构造签名串
  780. function constructSignatureString(appId: string, timestamp: string, nonceStr: string, prepayId: string): string {
  781. return `${appId}\n${timestamp}\n${nonceStr}\n${prepayId}\n`;
  782. }
  783. // 微信支付RSA2048|PKCS1|SHA256 APP调起支付签名
  784. async function rsaSign2048(message:string): Promise<Uint8Array> {
  785. try {
  786. let keyGen = cryptoFramework.createAsyKeyGenerator("RSA2048")
  787. let key = keyGen.convertPemKeySync(null, CommonConstants.WX_PAY_RSA_PRIVATE_KEY)
  788. let sign = cryptoFramework.createSign('RSA2048|PKCS1|SHA256');
  789. let msgBlob: cryptoFramework.DataBlob = {data: StrUtil.strToUint8Array(message)}
  790. sign.initSync(key.priKey)
  791. let signature = await sign.sign(msgBlob)
  792. return signature.data
  793. } catch (error) {
  794. console.error(error, `onecold error code: ${error.code}`)
  795. return new Uint8Array()
  796. }
  797. }
  798. // 定义接口请求参数类型
  799. interface WeChatOrderRequest {
  800. appid: string;
  801. mchid: string;
  802. description: string;
  803. out_trade_no: string;
  804. notify_url: string;
  805. amount: Amount;
  806. }
  807. function generateSignatureInfo(timestamp: string,nonceStr: string,body:string): string {
  808. const httpMethod = 'POST';
  809. const url = '/v3/pay/transactions/jsapi';
  810. // const timestamp = Math.floor(Date.now() / 1000).toString();
  811. // 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"}}';
  812. console.log("onecold rsasignRSA ", 'body: ' + body);
  813. const signatureString = `${httpMethod}\n${url}\n${timestamp}\n${nonceStr}\n${body}\n`;
  814. return signatureString
  815. }
  816. function getOutTradeNo(): string {
  817. const timestamp = Date.now().toString(); // 获取当前时间戳
  818. const randomPart = Math.floor(Math.random() * 1e16).toString().padStart(16, '0'); // 生成16位随机数
  819. const orderNumber = (timestamp + randomPart).slice(0, 32); // 确保长度为32位
  820. return orderNumber;
  821. }
  822. // 构造Authorization头
  823. function constructAuthorizationHeader(mchid: string,nonceStr:string,signature: string,
  824. timestamp:string,serialNo:string): string {
  825. return `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${serialNo}"`;
  826. }
  827. async function createWeChatOrderInfo(appid: string, mchid: string, description: string,amount: number,
  828. outTradeNo: string): Promise<string> {
  829. const url: string = 'https://api.mch.weixin.qq.com/v3/pay/transactions/app';
  830. let nonceStr = RandomUtil.getRandomStr(16,'abcdefghijklmnopqrstuvwxyz')
  831. const timestamp: number = Math.floor(Date.now() / 1000);
  832. // 构建请求体
  833. const body: WeChatOrderRequest = { appid: appid, mchid: mchid, description: description, out_trade_no: outTradeNo, notify_url: VipPage.WX_NOTIFY_URL, amount: { total: amount, currency: 'CNY'} };
  834. let generateSign:string= generateSignatureInfo(timestamp+'',nonceStr, JSON.stringify(body))
  835. console.log("onecold rsasignRSA ", 'generateSign: ' + generateSign);
  836. let signStr = await rsaSign2048(generateSign);
  837. let signStrBase64 = Base64Util.encodeToStrSync(signStr)
  838. let authorizationHeader = constructAuthorizationHeader(mchid,nonceStr,signStrBase64,timestamp+'',CommonConstants.WX_PAY_SERIALNO)
  839. console.log("onecold rsasignRSA ", 'signStr: ' + signStr);
  840. console.log("onecold rsasignRSA ", 'signStrBase64: ' + signStrBase64);
  841. console.log("onecold rsasignRSA ", 'authorizationHeader: ' + authorizationHeader);
  842. // 构建请求头
  843. const headers: Record<string, string> = {
  844. 'Authorization': authorizationHeader, // 替换为你的授权信息
  845. 'Accept': 'application/json',
  846. 'Content-Type': 'application/json',
  847. };
  848. const httpRequest: http.HttpRequest = http.createHttp();
  849. const options: http.HttpRequestOptions = {
  850. method: http.RequestMethod.POST,
  851. header: headers,
  852. extraData: JSON.stringify(body),
  853. readTimeout: 6000,
  854. connectTimeout: 6000,
  855. };
  856. try {
  857. const response: http.HttpResponse = await httpRequest.request(url, options);
  858. LogUtil.debug("onecold 微信支付 response.result=" + response.result)
  859. if (response.responseCode === 200) {
  860. // LogUtil.debug("onecold 微信支付 response.result=" + response.result)
  861. let responseData: WeChatOrderResponse;
  862. if (typeof response.result === 'string') {
  863. responseData = JSON.parse(response.result) as WeChatOrderResponse;
  864. } else if (response.result instanceof Object) {
  865. responseData = response.result as WeChatOrderResponse;
  866. } else {
  867. throw new Error('Unexpected response format');
  868. }
  869. const prepayId: string = responseData.prepay_id;
  870. console.log('WeChat Order Created, prepay_id:', prepayId);
  871. return prepayId;
  872. } else {
  873. console.error('Failed to create WeChat order:', response.responseCode, response.result);
  874. throw new Error(`HTTP Error: ${response.responseCode}`);
  875. }
  876. } catch (error) {
  877. console.error('Error creating WeChat order:', error);
  878. return '';
  879. }
  880. }