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.
 
 

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