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.
 
 

350 rivejä
15 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 { useNavigate } from "react-router-dom";
  11. import FpsIcon from "assets/images/icons/fps.svg";
  12. import { useLocation } from 'react-router-dom';
  13. import {paymentPath} from "auth/utils";
  14. import {currencyFormat} from "utils/FormatUtils";
  15. // import {poll} from "utils/Utils";
  16. import Loadable from 'components/Loadable';
  17. const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
  18. import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
  19. import {FormattedMessage} from "react-intl";
  20. const BackgroundHead = {
  21. backgroundImage: `url(${titleBackgroundImg})`,
  22. width: '100%',
  23. height: '100%',
  24. backgroundSize: 'contain',
  25. backgroundRepeat: 'no-repeat',
  26. backgroundColor: '#0C489E',
  27. backgroundPosition: 'right'
  28. }
  29. // ==============================|| DASHBOARD - DEFAULT ||============================== //
  30. const Index = () => {
  31. const navigate = useNavigate()
  32. const location = useLocation();
  33. const [locationData, setLocationData] = React.useState({});
  34. const [paymentData, setPaymentData] = React.useState({});
  35. const [onReady, setOnReady] = React.useState(false);
  36. const [responeData, setResponeDataData] = React.useState({});
  37. const [fpsTransctionData, setFpsTransctionData] = React.useState({});
  38. const [fpsmerchanttimeoutdatetime, setFpsmerchanttimeoutdatetime] = React.useState("");
  39. const [timeDownCount, setTimeDownCount] = React.useState(0);
  40. const [paymentId, setPaymentId] = React.useState("");
  41. const [fpsqrcodeurl, setFpsqrcodeurl] = React.useState("");
  42. const [fpsqrcodeurlPrd, setFpsqrcodeurlPrd] = React.useState("");
  43. const [browserType, setBrowserType] = React.useState("");
  44. const mobileBrowser = "Mobile";
  45. const desktopBrowser = "Desktop";
  46. const pasgPathPrd = 'https://fps.payapps.hkicl.com.hk'; //PRD
  47. const pasgPath = 'https://sim.fps.payapps.hkicl.com.hk'; //Testing
  48. const loadPaymentUrl = "/api/payment/wallet/fps";
  49. const cancelPaymentUrl = "/api/payment/cancelpayment";
  50. const paymentStatusApi = "/api/payment/status/";
  51. // const payloadUrl = "/api/payment/wallet/fps/enquiryfpspayload/";
  52. // const receiverUrl = "/noti-api/payment/payment-notification";
  53. //timer
  54. const currentTimer = React.useRef();
  55. const [time, setTime] = React.useState(0);
  56. React.useEffect(() => {
  57. // console.log (location.state)
  58. if(Object.keys(location.state).length > 0){
  59. // console.log (location.state)
  60. setLocationData(location.state)
  61. if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) {
  62. console.log('Mobile web browser');
  63. setBrowserType(mobileBrowser)
  64. // setFpsqrcodeurl(openPASGUrl)
  65. } else {
  66. console.log('Desktop web browser');
  67. setBrowserType(desktopBrowser)
  68. }
  69. }
  70. }, []);
  71. React.useEffect(() => {
  72. // console.log (locationData)
  73. if (Object.keys(locationData).length > 0){
  74. setPaymentData(locationData)
  75. // loadForm();
  76. }
  77. }, [locationData]);
  78. React.useEffect(() => {
  79. // console.log (paymentData)
  80. if (Object.keys(paymentData).length > 0){
  81. loadForm();
  82. }
  83. }, [paymentData]);
  84. React.useEffect(() => {
  85. // console.log(responeData)
  86. if(Object.keys(responeData).length > 0 && fpsmerchanttimeoutdatetime!=""){
  87. setFpsTransctionData(responeData)
  88. }
  89. }, [responeData]);
  90. React.useEffect(() => {
  91. // console.log(fpsTransctionData)
  92. if(Object.keys(fpsTransctionData).length > 0 ){
  93. setPaymentId(fpsTransctionData.paymentid)
  94. currentTimer.current = setInterval(() => {
  95. setTime((prevTime) => prevTime + 1);
  96. }, 1000);
  97. setOnReady(true);
  98. return () => clearInterval(currentTimer.current);
  99. }
  100. }, [fpsTransctionData]);
  101. const loadForm = () => {
  102. // const timeoutdatetime = "2023-10-26T09:04:30Z[UTC]"
  103. // const convertedDateString = timeoutdatetime.replace("[UTC]", "");
  104. // setFpsmerchanttimeoutdatetime(convertedDateString)
  105. // setPaymentid("C202310268000681")
  106. // setPaymentstatuscode("APPR")
  107. localStorage.removeItem("transactionid")
  108. localStorage.removeItem("webtoken")
  109. localStorage.setItem("transactionid", paymentData.transactionid)
  110. localStorage.setItem("webtoken", paymentData.webtoken)
  111. HttpUtils.post({
  112. url: paymentPath+loadPaymentUrl,
  113. params:{
  114. "transactionid": paymentData.transactionid,
  115. "webtoken": paymentData.webtoken,
  116. "paymentmethod":"04,BCFP,FPS",
  117. "order": {
  118. "totalamount":paymentData.amount,
  119. "currency":"HKD",
  120. "orderdetail":
  121. [
  122. {
  123. "itemid": "1",
  124. "qty":"1",
  125. "unitprice":paymentData.amount,
  126. "amount":paymentData.amount
  127. },
  128. ]
  129. },
  130. // "locale":"<locale>",
  131. // "eserviceid":"<eserviceid>"
  132. },
  133. onSuccess: function(responseData){
  134. /*
  135. {
  136. "paymentid": "<paymentid>",
  137. "paymentstatus": "<paymentstatus>",
  138. "fpsmerchanttimeoutdatetime": <fpsmerchanttimeoutdatetime>,
  139. "fpsqrcodeimgbase64": "<fpsqrcodeimgbase64>",
  140. "fpsqrcodeurl": "<fpsqrcodeurl>"
  141. }
  142. */
  143. setResponeDataData(responseData)
  144. const timeoutdatetime = responseData.fpsmerchanttimeoutdatetime
  145. const searchString = "[UTC]";
  146. let convertedDateString = "";
  147. if ( timeoutdatetime.toString().includes(searchString) ){
  148. convertedDateString = timeoutdatetime.replace("[UTC]", "");
  149. } else {
  150. convertedDateString = timeoutdatetime;
  151. }
  152. setFpsmerchanttimeoutdatetime(convertedDateString)
  153. const parsedUrl = new URL(responseData.fpsqrcodeurl);
  154. const fpsqrcodeurl = "https://"+window.location.hostname+'/payment'+parsedUrl.pathname;
  155. const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback='
  156. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+paymentData.transactionid+"&WEB_TOKEN="+paymentData.webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  157. const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback='
  158. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+paymentData.transactionid+"&WEB_TOKEN="+paymentData.webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  159. setFpsqrcodeurl(openPASGUrl)
  160. setFpsqrcodeurlPrd(openPASGUrlPrd)
  161. }
  162. });
  163. }
  164. const getPaymentStatus = () => {
  165. if(Object.keys(paymentData).length > 0){
  166. HttpUtils.post({
  167. url: paymentPath+paymentStatusApi+paymentData.transactionid,
  168. params:{
  169. "apprefid": paymentData.transactionid,
  170. "webtoken": paymentData.webtoken,
  171. },
  172. onSuccess: function(responseData){
  173. const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode;
  174. if (paymentstatuscode != "" && paymentstatuscode != "INPR" ){
  175. if (paymentstatuscode === 'APPR') {
  176. // const timestamp = Date.now();
  177. navigate('/paymentPage/fps/ackpage');
  178. } else if (paymentstatuscode === 'CANC') {
  179. // const timestamp = Date.now();
  180. navigate('/paymentPage/fps/ackpage');
  181. } else {
  182. // window.top.location.href = paymentPath + payment.config.errPagePath;
  183. alert("ERROR")
  184. }
  185. }
  186. },
  187. onError: function(){
  188. cancelPayment()
  189. // clearInterval(currentTimer.current);
  190. }
  191. });
  192. }
  193. };
  194. React.useEffect(() => {
  195. const timeOutDate = new Date(fpsmerchanttimeoutdatetime);
  196. const currentTime = new Date;
  197. const timedowncount = Math.round((timeOutDate.getTime() - currentTime.getTime()) / 1000);
  198. setTimeDownCount(timedowncount);
  199. // console.log(time)
  200. // console.log(timeOutDate)
  201. // console.log(currentTime)
  202. // console.log(timeOutDate.getTime()-currentTime.getTime())
  203. getPaymentStatus();
  204. if (timeOutDate.getTime()<currentTime.getTime()){
  205. console.log("stop");
  206. clearInterval(currentTimer.current);
  207. cancelPayment()
  208. }
  209. },[time])
  210. const cancelPayment = ()=>{
  211. if (Object.keys(paymentData).length>0){
  212. HttpUtils.post({
  213. url: paymentPath+cancelPaymentUrl,
  214. params:{
  215. "transactionid": paymentData.transactionid,
  216. "webtoken": paymentData.webtoken,
  217. "paymentid": fpsTransctionData.paymentid
  218. },
  219. onSuccess: function(){
  220. navigate('/paymentPage/fps/ackpage');
  221. }
  222. });
  223. }
  224. }
  225. const mobliePayment = ()=>{
  226. window.location.assign(fpsqrcodeurl);
  227. }
  228. const mobliePaymentPrd = ()=>{
  229. window.location.assign(fpsqrcodeurlPrd);
  230. }
  231. return (
  232. !onReady ?
  233. <LoadingComponent />
  234. :
  235. (
  236. <Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" >
  237. <Grid item xs={12} width="100%">
  238. <div style={BackgroundHead} width="100%">
  239. <Stack direction="row" height='70px'>
  240. <Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
  241. <FormattedMessage id="publicNoticePaymentFPSPay"/>
  242. </Typography>
  243. </Stack>
  244. </div>
  245. </Grid>
  246. {/*row 1*/}
  247. <Grid item xs={12} md={12} >
  248. <Grid container justifyContent="flex-start" alignItems="center" >
  249. <center>
  250. <Grid item xs={12} md={12} >
  251. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  252. <img src={FpsIcon} width="80" height="80" alt="FPS"></img>
  253. <br />
  254. <FormattedMessage id="payTotal"/>
  255. <br />
  256. {"HK$ " + currencyFormat(paymentData.amount)}
  257. </Typography>
  258. {browserType==mobileBrowser?
  259. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  260. <Button
  261. component="span"
  262. variant="contained"
  263. size="large"
  264. color="primary"
  265. onClick={()=>{
  266. mobliePayment();
  267. }}
  268. sx={{ m: 4 }}
  269. >請選擇支付程式付款-Testing</Button>
  270. <Button
  271. component="span"
  272. variant="contained"
  273. size="large"
  274. color="primary"
  275. onClick={()=>{
  276. mobliePaymentPrd();
  277. }}
  278. sx={{ m: 4 }}
  279. >請選擇支付程式付款-PRD</Button>
  280. </Typography>
  281. :
  282. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  283. 請掃描以下二維碼
  284. <br />
  285. <img src={fpsTransctionData.fpsqrcodeimgbase64} alt="QR Code"/>
  286. <br />
  287. {"["+paymentId+"]"}
  288. <br/>
  289. 二維碼有效期限3分鐘
  290. <br />
  291. 請在規定時間內完成付款流程
  292. <br />
  293. {"剩餘時間: "+timeDownCount+ "秒"}
  294. </Typography>
  295. }
  296. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  297. <Button
  298. component="span"
  299. variant="contained"
  300. size="large"
  301. color="error"
  302. onClick={()=>{
  303. cancelPayment();
  304. }}
  305. sx={{ m: 4 }}
  306. >
  307. <FormattedMessage id="payCancel"/>
  308. </Button>
  309. </Typography>
  310. </Grid>
  311. </center>
  312. </Grid>
  313. </Grid>
  314. {/*row 2*/}
  315. </Grid >
  316. )
  317. );
  318. };
  319. export default Index;