VipPage.ets 37 KB

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