You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

481 lines
24 KiB

  1. // material-ui
  2. import {
  3. Grid,
  4. Typography,
  5. Stack,
  6. Button,
  7. } from '@mui/material';
  8. import * as React from "react";
  9. import * as HttpUtils from "utils/HttpUtils";
  10. import * as UrlUtils from "utils/ApiPathConst";
  11. import { useNavigate } from "react-router-dom";
  12. import FpsIcon from "assets/images/icons/fps.svg";
  13. import expiredQrcodeEN from "assets/images/icons/expiredQrcodeEN.png";
  14. import expiredQrcodeZH from "assets/images/icons/expiredQrcodeZH.png";
  15. import expiredQrcodeCN from "assets/images/icons/expiredQrcodeCN.png";
  16. import { useLocation } from 'react-router-dom';
  17. // import {paymentPath} from "auth/utils";
  18. import {currencyFormat} from "utils/FormatUtils";
  19. // import {poll} from "utils/Utils";
  20. import Loadable from 'components/Loadable';
  21. const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
  22. import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
  23. import {FormattedMessage, useIntl} from "react-intl";
  24. const BackgroundHead = {
  25. backgroundImage: `url(${titleBackgroundImg})`,
  26. width: '100%',
  27. height: '100%',
  28. backgroundSize: 'contain',
  29. backgroundRepeat: 'no-repeat',
  30. backgroundColor: '#0C489E',
  31. backgroundPosition: 'right'
  32. }
  33. // ==============================|| DASHBOARD - DEFAULT ||============================== //
  34. const Index = () => {
  35. const navigate = useNavigate()
  36. const location = useLocation();
  37. const intl = useIntl();
  38. const { locale } = intl;
  39. const [locationData, setLocationData] = React.useState({});
  40. const [paymentData, setPaymentData] = React.useState({});
  41. const [onReady, setOnReady] = React.useState(false);
  42. const [qrCodeTimeout, setqrCodeTimeout] = React.useState(false);
  43. const [paymentStatusCode, setPaymentStatusCode] = React.useState("");
  44. const [expiredQrcode, setExpiredQrcode] = React.useState(expiredQrcodeEN);
  45. const [responeData, setResponeDataData] = React.useState({});
  46. const [fpsTransctionData, setFpsTransctionData] = React.useState({});
  47. const [fpsmerchanttimeoutdatetime, setFpsmerchanttimeoutdatetime] = React.useState("");
  48. const [timeDownCount, setTimeDownCount] = React.useState(0);
  49. const [paymentId, setPaymentId] = React.useState("");
  50. const [fpsqrcodeurl, setFpsqrcodeurl] = React.useState("");
  51. const [fpsqrcodeurlPrd, setFpsqrcodeurlPrd] = React.useState("");
  52. const [fpsqrcodeurlFps, setFpsqrcodeurlFps] = React.useState("");
  53. const [browserType, setBrowserType] = React.useState("");
  54. const [sysEnv, setSysEnv] = React.useState("");
  55. const mobileBrowser = "Mobile";
  56. const desktopBrowser = "Desktop";
  57. const pasgPathPrd = 'https://fps.payapps.hkicl.com.hk'; //PRD
  58. const pasgPath = 'https://sim.fps.payapps.hkicl.com.hk'; //Testing
  59. // const loadPaymentUrl = "/api/payment/wallet/fps";
  60. // const cancelPaymentUrl = "/api/payment/cancelpayment";
  61. // const paymentStatusApi = "/api/payment/status/";
  62. // const payloadUrl = "/api/payment/wallet/fps/enquiryfpspayload/";
  63. // const receiverUrl = "/noti-api/payment/payment-notification";
  64. //timer
  65. const currentTimer = React.useRef();
  66. const [time, setTime] = React.useState(0);
  67. React.useEffect(() => {
  68. // console.log (location.state)
  69. if(Object.keys(location.state).length > 0){
  70. // console.log (location.state)
  71. setLocationData(location.state)
  72. setBrowserType(desktopBrowser)
  73. if (localStorage.getItem('userData') != null){
  74. const userId = JSON.parse(localStorage.getItem('userData')).id
  75. if (userId === 13){
  76. if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) {
  77. // console.log('Mobile web browser');
  78. setBrowserType(mobileBrowser)
  79. // setFpsqrcodeurl(openPASGUrl)
  80. } else {
  81. // console.log('Desktop web browser');
  82. setBrowserType(desktopBrowser)
  83. }
  84. }
  85. }
  86. }
  87. }, []);
  88. React.useEffect(() => {
  89. // console.log (locationData)
  90. if (Object.keys(locationData).length > 0){
  91. setPaymentData(locationData)
  92. // loadForm();
  93. }
  94. }, [locationData]);
  95. React.useEffect(() => {
  96. // console.log (locationData)
  97. if (locale === 'zh-HK'){
  98. setExpiredQrcode(expiredQrcodeZH)
  99. } else if (locale === 'en'){
  100. setExpiredQrcode(expiredQrcodeEN)
  101. } else {
  102. setExpiredQrcode(expiredQrcodeCN)
  103. }
  104. }, [locale]);
  105. React.useEffect(() => {
  106. // console.log (paymentData)
  107. if (Object.keys(paymentData).length > 0){
  108. loadForm();
  109. }
  110. }, [paymentData]);
  111. React.useEffect(() => {
  112. // console.log(responeData)
  113. if(Object.keys(responeData).length > 0 && fpsmerchanttimeoutdatetime!=""){
  114. setFpsTransctionData(responeData)
  115. }
  116. }, [responeData]);
  117. React.useEffect(() => {
  118. // console.log(fpsTransctionData)
  119. if(Object.keys(fpsTransctionData).length > 0 ){
  120. setPaymentId(fpsTransctionData.paymentid)
  121. currentTimer.current = setInterval(() => {
  122. setTime((prevTime) => prevTime + 1);
  123. }, 1000);
  124. setOnReady(true);
  125. return () => clearInterval(currentTimer.current);
  126. }
  127. }, [fpsTransctionData]);
  128. const loadForm = () => {
  129. const webtoken = paymentData.webtoken;
  130. const transactionid = paymentData.transactionid;
  131. console.log(webtoken)
  132. console.log(transactionid)
  133. localStorage.removeItem("transactionid")
  134. localStorage.removeItem("webtoken")
  135. localStorage.setItem("transactionid", transactionid)
  136. localStorage.setItem("webtoken", webtoken)
  137. HttpUtils.post({
  138. url: UrlUtils.LOAD_PAYMENT_FPS_URL,
  139. params:{
  140. "transactionid": transactionid,
  141. "webtoken": webtoken,
  142. "paymentmethod":"04,BCFP,FPS",
  143. "order": {
  144. "totalamount":paymentData.amount,
  145. "currency":"HKD",
  146. "orderdetail":
  147. [
  148. {
  149. "itemid": "1",
  150. "qty":"1",
  151. "unitprice":paymentData.amount,
  152. "amount":paymentData.amount
  153. },
  154. ]
  155. },
  156. // "locale":"<locale>",
  157. // "eserviceid":"<eserviceid>"
  158. },
  159. onSuccess: function(responseData){
  160. /*
  161. {
  162. "paymentid": "<paymentid>",
  163. "paymentstatus": "<paymentstatus>",
  164. "fpsmerchanttimeoutdatetime": <fpsmerchanttimeoutdatetime>,
  165. "fpsqrcodeimgbase64": "<fpsqrcodeimgbase64>",
  166. "fpsqrcodeurl": "<fpsqrcodeurl>"
  167. }
  168. */
  169. setResponeDataData(responseData)
  170. const timeoutdatetime = responseData.fpsmerchanttimeoutdatetime
  171. setSysEnv(responseData.sysEnv)
  172. const searchString = "[UTC]";
  173. let convertedDateString = "";
  174. if ( timeoutdatetime.toString().includes(searchString) ){
  175. convertedDateString = timeoutdatetime.replace("[UTC]", "");
  176. } else {
  177. convertedDateString = timeoutdatetime;
  178. }
  179. setFpsmerchanttimeoutdatetime(convertedDateString)
  180. const parsedUrl = new URL(responseData.fpsqrcodeurl);
  181. const fpsqrcodeurl = "https://"+window.location.hostname+'/payment'+parsedUrl.pathname;
  182. const fpsqrcodeurlwithFps = "https://"+"fps."+window.location.hostname+parsedUrl.pathname;
  183. console.log(parsedUrl)
  184. console.log(fpsqrcodeurl)
  185. console.log(fpsqrcodeurlwithFps)
  186. const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  187. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId"));
  188. const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  189. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId"));
  190. const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  191. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId"));
  192. // const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  193. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  194. // const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  195. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  196. // const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  197. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  198. setFpsqrcodeurl(openPASGUrl)
  199. setFpsqrcodeurlPrd(openPASGUrlPrd)
  200. setFpsqrcodeurlFps(openPASGUrlPrdFps)
  201. }
  202. });
  203. //testing
  204. // const timeoutdatetime = "2024-11-18T07:04:35Z[UTC]"
  205. // const convertedDateString = timeoutdatetime.replace("[UTC]", "");
  206. // setFpsmerchanttimeoutdatetime(convertedDateString)
  207. // setPaymentId("C202310268000681")
  208. // const responseData=
  209. // {
  210. // "paymentid": "C202310268000681",
  211. // "paymentstatus": "INPR",
  212. // "fpsmerchanttimeoutdatetime": "2024-11-18T07:04:35Z[UTC]",
  213. // "fpsqrcodeimgbase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAuyklEQVR4Xu3dfcy/d1nff2IAAAAASUVORK5CYII=",
  214. // "fpsqrcodeurl": "http://127.0.0.1:8080/api/payment/wallet/fps/enquiryfpspayload/vm.JKDDlTOavR3ASviSwUnS1Lw4-"
  215. // }
  216. // setResponeDataData(responseData)
  217. // const parsedUrl = new URL(responseData.fpsqrcodeurl);
  218. // const fpsqrcodeurl = "https://"+window.location.hostname+'/payment'+parsedUrl.pathname;
  219. // const fpsqrcodeurlwithFps = "https://"+"fps."+window.location.hostname+parsedUrl.pathname;
  220. // console.log(parsedUrl)
  221. // console.log(fpsqrcodeurl)
  222. // console.log(fpsqrcodeurlwithFps)
  223. // const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback='
  224. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  225. // const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback='
  226. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  227. // const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  228. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  229. // setFpsqrcodeurl(openPASGUrl)
  230. // setFpsqrcodeurlPrd(openPASGUrlPrd)
  231. // setFpsqrcodeurlFps(openPASGUrlPrdFps)
  232. }
  233. const getPaymentStatus = () => {
  234. if(Object.keys(paymentData).length > 0){
  235. HttpUtils.post({
  236. url: UrlUtils.PAYMENT_STATUS_API,
  237. params:{
  238. "apprefid": paymentData.transactionid,
  239. // "webtoken": paymentData.webtoken,
  240. "transactionid":Number(paymentData.transactionid)
  241. },
  242. onSuccess: function(responseData){
  243. const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode;
  244. setPaymentStatusCode(paymentstatuscode)
  245. if (paymentstatuscode != "" && paymentstatuscode != "INPR" ){
  246. if (paymentstatuscode === 'APPR') {
  247. // const timestamp = Date.now();
  248. let page = '/paymentPage/fps/ackpage';
  249. let stateParams = { state: { transactionid: paymentData.transactionid} }
  250. navigate(page, stateParams);
  251. } else if (paymentstatuscode === 'CANC') {
  252. // const timestamp = Date.now();
  253. let page = '/paymentPage/fps/ackpage';
  254. let stateParams = { state: { transactionid: paymentData.transactionid} }
  255. navigate(page, stateParams);
  256. } else {
  257. // window.top.location.href = paymentPath + payment.config.errPagePath;
  258. alert("ERROR")
  259. }
  260. }
  261. },
  262. onError: function(){
  263. alert("ERROR")
  264. // cancelPayment()
  265. // clearInterval(currentTimer.current);
  266. }
  267. });
  268. }
  269. };
  270. React.useEffect(() => {
  271. const timeOutDate = new Date(fpsmerchanttimeoutdatetime);
  272. const currentTime = new Date;
  273. const timedowncount = Math.round((timeOutDate.getTime() - currentTime.getTime()) / 1000);
  274. // console.log(time)
  275. // console.log(timeOutDate)
  276. // console.log(currentTime)
  277. // console.log(timeOutDate.getTime()-currentTime.getTime())
  278. if (browserType === desktopBrowser){
  279. getPaymentStatus();
  280. if (timeOutDate.getTime()<currentTime.getTime()){
  281. // console.log("stop");
  282. clearInterval(currentTimer.current);
  283. setqrCodeTimeout(true)
  284. setTimeDownCount(0);
  285. cancelPayment()
  286. }else{
  287. setTimeDownCount(timedowncount);
  288. }
  289. }
  290. },[time])
  291. const cancelPayment = ()=>{
  292. if (Object.keys(paymentData).length>0){
  293. getPaymentStatus()
  294. if (paymentStatusCode === "INPR"){
  295. HttpUtils.post({
  296. url: UrlUtils.CANCEL_PAYMENT_URL,
  297. params:{
  298. "transactionid": paymentData.transactionid,
  299. "webtoken": paymentData.webtoken,
  300. "paymentid": fpsTransctionData.paymentid
  301. },
  302. onSuccess: function(){
  303. navigate('/paymentPage/fps/ackpage');
  304. let page = '/paymentPage/fps/ackpage';
  305. let stateParams = { state: { transactionid: paymentData.transactionid} }
  306. navigate(page, stateParams);
  307. }
  308. });
  309. }
  310. }
  311. }
  312. const mobliePayment = ()=>{
  313. window.location.assign(fpsqrcodeurl);
  314. }
  315. const mobliePaymentPrd = ()=>{
  316. window.location.assign(fpsqrcodeurlPrd);
  317. }
  318. const mobliePaymentFps = ()=>{
  319. window.location.assign(fpsqrcodeurlFps);
  320. }
  321. return (
  322. !onReady ?
  323. <Grid container sx={{ minHeight: '87vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center">
  324. <Grid item>
  325. <LoadingComponent />
  326. </Grid>
  327. </Grid>
  328. :
  329. (
  330. <Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" >
  331. <Grid item xs={12} width="100%">
  332. <div style={BackgroundHead} width="100%">
  333. <Stack direction="row" height='70px'>
  334. <Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
  335. <FormattedMessage id="publicNoticePaymentFPSPay"/>
  336. </Typography>
  337. </Stack>
  338. </div>
  339. </Grid>
  340. {/*row 1*/}
  341. <Grid item xs={12} md={12} >
  342. <Grid container justifyContent="flex-start" alignItems="center" >
  343. <center>
  344. <Grid item xs={12} md={12} >
  345. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  346. <FormattedMessage id="payAlert"/>
  347. <br /><br />
  348. <img src={FpsIcon} width="80" height="80" alt="FPS"></img>
  349. <br />
  350. <FormattedMessage id="payTotalDeatail"/>
  351. <br />
  352. {"HK$ " + currencyFormat(paymentData.amount)}
  353. </Typography>
  354. {browserType==mobileBrowser?
  355. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  356. {
  357. sysEnv=="prod"?
  358. <Button
  359. component="span"
  360. variant="contained"
  361. size="large"
  362. color="primary"
  363. onClick={()=>{
  364. mobliePaymentPrd();
  365. }}
  366. sx={{ m: 4 }}
  367. >請選擇支付程式付款</Button>
  368. :
  369. <>
  370. <Button
  371. component="span"
  372. variant="contained"
  373. size="large"
  374. color="primary"
  375. onClick={()=>{
  376. mobliePayment();
  377. }}
  378. sx={{ m: 4 }}
  379. >請選擇支付程式付款-Testing</Button>
  380. <Button
  381. component="span"
  382. variant="contained"
  383. size="large"
  384. color="primary"
  385. onClick={()=>{
  386. mobliePaymentFps();
  387. }}
  388. sx={{ m: 4 }}
  389. >請選擇支付程式付款-fps prefix</Button>
  390. </>
  391. }
  392. </Typography>
  393. :
  394. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  395. <FormattedMessage id="fpsQrcodeTitle1"/>
  396. <br />
  397. {
  398. !qrCodeTimeout?
  399. <img src={fpsTransctionData.fpsqrcodeimgbase64} alt="QR Code"/>
  400. :<img src={expiredQrcode} alt="Expired QR Code"/>
  401. }
  402. <br />
  403. {"["+paymentId+"]"}
  404. <br/>
  405. {
  406. timeDownCount<=0?
  407. <FormattedMessage id="fpsQrcodeExpired"/>:
  408. <>
  409. <FormattedMessage id="fpsQrcodeTitle2"/>
  410. <br />
  411. <FormattedMessage id="fpsQrcodeTitle3"/>
  412. <br />
  413. <FormattedMessage id="fpsQrcodeTitle4"/>&nbsp;
  414. {timeDownCount}&nbsp;
  415. <FormattedMessage id="fpsQrcodeTitle5"/>
  416. </>
  417. }
  418. </Typography>
  419. }
  420. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  421. <Button
  422. component="span"
  423. variant="contained"
  424. size="large"
  425. color="error"
  426. onClick={()=>{
  427. cancelPayment();
  428. }}
  429. sx={{ m: 4 }}
  430. >
  431. <FormattedMessage id="payCancel"/>
  432. </Button>
  433. </Typography>
  434. </Grid>
  435. </center>
  436. </Grid>
  437. </Grid>
  438. {/*row 2*/}
  439. </Grid >
  440. )
  441. );
  442. };
  443. export default Index;