No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 

520 líneas
23 KiB

  1. import {
  2. useEffect,
  3. useState
  4. } from "react";
  5. import * as React from "react";
  6. import {getBowserType} from "auth/utils";
  7. // material-ui
  8. import {
  9. Button,
  10. FormLabel,
  11. Stack,
  12. Typography,
  13. Dialog,
  14. DialogActions,
  15. DialogContent,
  16. DialogContentText,
  17. DialogTitle,
  18. Grid,
  19. Box
  20. } from '@mui/material';
  21. import { useFormik, FormikProvider } from 'formik';
  22. import * as yup from 'yup';
  23. import Loadable from 'components/Loadable';
  24. const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
  25. import * as FormatUtils from "utils/FormatUtils";
  26. import SafeHtml from 'components/SafeHtml';
  27. import VisaIcon from "assets/images/icons/visacard.svg";
  28. import MasterIcon from "assets/images/icons/mastercard.svg";
  29. import JcbIcon from "assets/images/icons/jcb.svg";
  30. import UnionPayIcon from "assets/images/icons/unionpay.svg";
  31. import PpsIcon from "assets/images/icons/ppshk.svg";
  32. import FpsIcon from "assets/images/icons/fps.svg";
  33. import {FormattedMessage, useIntl} from "react-intl";
  34. import { PRIMARY_CONTAINED_BUTTON_SX, PAY_CONTAINED_BUTTON_SX } from "themes/colorConst";
  35. import * as HttpUtils from "utils/HttpUtils"
  36. import * as UrlUtils from "utils/ApiPathConst"
  37. const MultiPaymentWindow = (props) => {
  38. const intl = useIntl();
  39. const windowTitle = intl.formatMessage({id: 'selectPaymentMethod'});
  40. // const [content, setContent] = useState();
  41. const [loadtTransactionData, setLoadtTransactionData] = useState({});
  42. const [loadAvailableMethodData, setLoadAvailableMethodData] = useState([]);
  43. const [paymentMethod, setPaymentMethod] = useState("");
  44. const [isLimit, setIsLimit] = useState(false);
  45. const [isPPSLimit, setIsPPSLimit] = useState(false);
  46. const [transactionData, setTransactionData] = useState({});
  47. const [availableMethodData, setAvailableMethodData] = useState([]);
  48. const [fpsClass, setFpsClass] = useState("");
  49. const [visaClass, setVisaClass] = useState("");
  50. const [mastercardClass, setMastercardClass] = useState("");
  51. const [jCBClass, setJCBClass] = useState("");
  52. const [unionPayClass, setUnionPayClass] = useState("");
  53. const [pPSClass, setPPSlass] = useState("");
  54. const [filteredPaymentMethod, setFilteredPaymentMethod] = useState([]);
  55. const [onReady, setOnReady] = useState(false);
  56. const [paymentHoldedErrText, setPaymentHoldedErrText] = React.useState("");
  57. const [paymentHoldedErr, setPaymentHoldedErr] = React.useState(false);
  58. const [isPaying, setIsPaying] = React.useState(false);
  59. const payingRef = React.useRef(false);
  60. const mobileBrowser = "Mobile";
  61. useEffect(() => {
  62. // console.log(props.transactionData)
  63. if(Object.keys(props.transactionData).length > 0){
  64. setLoadtTransactionData(props.transactionData)
  65. // console.log(props.browserType)
  66. }
  67. }, [props.transactionData]);
  68. useEffect(() => {
  69. // console.log(props.availableMethods)
  70. if(props.availableMethods.length > 0){
  71. setLoadAvailableMethodData(props.availableMethods)
  72. }
  73. }, [props.availableMethods]);
  74. useEffect(() => {
  75. if(Object.keys(loadtTransactionData).length > 0){
  76. setTransactionData(loadtTransactionData)
  77. }
  78. }, [loadtTransactionData]);
  79. useEffect(() => {
  80. if(Object.keys(transactionData).length > 0&&props.availableMethods.length > 0){
  81. setLoadAvailableMethodData(props.availableMethods)
  82. }
  83. }, [transactionData]);
  84. useEffect(() => {
  85. if(loadAvailableMethodData.length > 0){
  86. setAvailableMethodData(loadAvailableMethodData)
  87. }
  88. }, [loadAvailableMethodData]);
  89. useEffect(() => {
  90. if(filteredPaymentMethod!= undefined && filteredPaymentMethod.length>0){
  91. setOnReady(true)
  92. } else if(filteredPaymentMethod!= undefined && Object.keys(filteredPaymentMethod).length > 0){
  93. setOnReady(true)
  94. }
  95. }, [filteredPaymentMethod]);
  96. useEffect(() => {
  97. const availableMethod = availableMethodData;
  98. if(props.availableMethods.length > 0){
  99. const filteringPaymentMethod = availableMethod.filter(obj => {
  100. if (obj.supportedcard && obj.supportedcard.includes(paymentMethod)) {
  101. return obj.subtype === "CreditCard";
  102. }
  103. return obj.subtype === paymentMethod;
  104. });
  105. if (isLimit && filteringPaymentMethod!= undefined && filteringPaymentMethod.length>0){
  106. if (paymentMethod == "FPS" || paymentMethod == "PPS"){
  107. filteringPaymentMethod[0].pointstonote = paymentMethod + intl.formatMessage({id: 'paymentLimitPrice1'})
  108. setFilteredPaymentMethod(filteringPaymentMethod[0]);
  109. } else if (isLimit) {
  110. filteringPaymentMethod[0].pointstonote = paymentMethod + intl.formatMessage({id: 'paymentLimitPrice2'})
  111. setFilteredPaymentMethod(filteringPaymentMethod[0]);
  112. }
  113. }
  114. if (!isLimit && filteringPaymentMethod!= undefined && filteringPaymentMethod.length>0){
  115. setFilteredPaymentMethod(filteringPaymentMethod);
  116. }
  117. if(isPPSLimit && filteringPaymentMethod!= undefined && filteringPaymentMethod.length>0 && paymentMethod =="PPS"){
  118. filteringPaymentMethod[0].pointstonote = paymentMethod + intl.formatMessage({id: 'paymentLimitPPS'})
  119. setFilteredPaymentMethod(filteringPaymentMethod[0]);
  120. }
  121. setFpsClass(paymentMethod == "FPS" || paymentMethod == "" ? "" : "grayscale")
  122. setVisaClass(paymentMethod == "Visa" || paymentMethod == "" ? "" : "grayscale")
  123. setMastercardClass(paymentMethod == "MasterCard" || paymentMethod == "" ? "" : "grayscale")
  124. setJCBClass(paymentMethod == "JCB" || paymentMethod == "" ? "" : "grayscale")
  125. setUnionPayClass(paymentMethod == "UnionPay" || paymentMethod == "" ? "" : "grayscale")
  126. setPPSlass(paymentMethod == "PPS" || paymentMethod == "" ? "" : "grayscale")
  127. }
  128. }, [paymentMethod]);
  129. const releasePayLock = () => {
  130. payingRef.current = false;
  131. setIsPaying(false);
  132. };
  133. const selectedPaymentMethodHandle = (method) => () =>{
  134. if (payingRef.current) return;
  135. if (method != paymentMethod){
  136. resetForm()
  137. let totalAmount = props.totalAmount;
  138. // totalAmount = 99999999.99
  139. // totalAmount = 0.01
  140. const paymentLimitList = props.paymentLimit
  141. let limitRecordList;
  142. switch (method) {
  143. case "Visa":
  144. case "JCB":
  145. case "MasterCard":
  146. limitRecordList = paymentLimitList.creditCardLimitRecord;
  147. break;
  148. case "FPS":
  149. limitRecordList = paymentLimitList.fpsLimitRecord;
  150. break;
  151. case "PPS":
  152. limitRecordList = paymentLimitList.ppsbLimitRecord;
  153. break;
  154. case "UnionPay":
  155. limitRecordList = paymentLimitList.unionPlayLimitRecord;
  156. break;
  157. default:
  158. break;
  159. }
  160. if (totalAmount >= limitRecordList.minLimit && totalAmount <= limitRecordList.maxLimit) {
  161. setIsLimit(false)
  162. } else {
  163. setIsLimit(true)
  164. }
  165. if (getBowserType() === "PC_Browser"){
  166. setPaymentMethod(method)
  167. props.setSelectedPaymentMethod(method);
  168. setIsPPSLimit(false)
  169. } else if (getBowserType() !== "PC_Browser" && method !== "PPS"){
  170. setPaymentMethod(method)
  171. props.setSelectedPaymentMethod(method);
  172. } else {
  173. setPaymentMethod(method)
  174. setIsPPSLimit(true)
  175. }
  176. }
  177. };
  178. const confirmPaymentHandle = () => () =>{
  179. handlePaymentCheck()
  180. };
  181. const handlePaymentCheck = () => {
  182. if (payingRef.current) return;
  183. payingRef.current = true;
  184. setIsPaying(true);
  185. let appIdList = props.appIds
  186. // console.log(props.appIds)
  187. // console.log(appIdList)
  188. HttpUtils.post({
  189. url: UrlUtils.PAYMENT_CHECK,
  190. params: {
  191. appIds: appIdList
  192. },
  193. onSuccess: (responseData) => {
  194. if (!payingRef.current) return;
  195. const latestData = {};
  196. responseData.forEach(item => {
  197. const { appId, timeDiff } = item;
  198. if (latestData[appId] === undefined || timeDiff < latestData[appId].timeDiff) {
  199. latestData[appId] = item;
  200. }
  201. });
  202. const latestDataObjects = Object.values(latestData);
  203. // const filteredData = latestDataObjects.filter(item => item.timeDiff > 20 || item.status !== "APPR");
  204. const filteredData = latestDataObjects.filter(item => item.timeDiff > 30 || item.status == "CANC" || item.status == "REJT");
  205. const filteredAppIds = filteredData.map(item => item.appId);
  206. const appIdsNotInData = appIdList.filter(appId => !latestDataObjects.some(item => item.appId === appId));
  207. const combinedAppIdsArray = [...appIdsNotInData, ...filteredAppIds];
  208. const readyToPayment = appIdList.every(appId => combinedAppIdsArray.includes(appId));
  209. if (readyToPayment){
  210. props.setConfirmPayment(true);
  211. return;
  212. }else{
  213. const appIdsInData = appIdList.filter(appId => !combinedAppIdsArray.some(item => item === appId));
  214. const HoldingApplication = latestDataObjects.filter(item => appIdsInData.includes(item.appId));
  215. const resultString = HoldingApplication.map(item => item.appNo).join(' , ');
  216. setPaymentHoldedErrText(resultString);
  217. // setPaymentHoldedErrText(intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: record.appNo }));
  218. releasePayLock();
  219. setPaymentHoldedErr(true);
  220. }
  221. },
  222. onFail: () => {
  223. releasePayLock();
  224. },
  225. onError: () => {
  226. releasePayLock();
  227. }
  228. });
  229. };
  230. const closeHandle = () => () =>{
  231. releasePayLock();
  232. resetForm()
  233. props.setOpen(false)
  234. };
  235. const resetForm = () =>{
  236. setOnReady(false)
  237. setPaymentMethod("")
  238. setFilteredPaymentMethod([])
  239. setIsLimit(false)
  240. setIsPPSLimit(false)
  241. };
  242. useEffect(() => {
  243. if(props.selectedPaymentMethod === ""){
  244. setPaymentMethod("")
  245. }
  246. }, [availableMethodData]);
  247. useEffect(() => {
  248. if (!props.confirmPayment) {
  249. releasePayLock();
  250. }
  251. }, [props.confirmPayment]);
  252. const formik = useFormik({
  253. initialValues: ({
  254. username: '',
  255. }),
  256. validationSchema: yup.object().shape({
  257. }),
  258. });
  259. return (
  260. <Dialog
  261. open={props.open}
  262. onClose={closeHandle()}
  263. fullWidth={true}
  264. maxWidth={'xl'}
  265. fullScreen={props.isFullScreen}
  266. >
  267. <DialogTitle sx={{ pb: 1, pt: 2 }}>
  268. <Typography variant="h4">
  269. {windowTitle}
  270. </Typography>
  271. </DialogTitle>
  272. <FormikProvider value={formik}>
  273. <form>
  274. <DialogContent sx={{ pt: 1 }}>
  275. <DialogContentText component="div" sx={{ m: 0 }}>
  276. <FormLabel component="div" sx={{ fontSize: "18px", color: "#000000", textAlign: "left", width: "100%", paddingLeft: 0, mb: 0 }}>
  277. <Box component="ul" sx={{ m: 0, pl: 2.5 }}>
  278. <li><FormattedMessage id="paymentProcessLimited1"/></li>
  279. <li><FormattedMessage id="paymentProcessLimited2"/></li>
  280. <li><FormattedMessage id="paymentProcessLimited3"/></li>
  281. </Box>
  282. </FormLabel>
  283. <Grid item xs={12} md={12} sx={{ pt: 2 }} style={{ height: '100%' }} width="100%">
  284. <Box xs={12} md={12} sx={{ p: 4, border: '3px solid #eee', borderRadius: '10px' }} >
  285. <Grid container justifyContent="flex-start" alignItems="left" >
  286. <center>
  287. <Grid item xs={12} md={12} width="100%">
  288. <Typography variant="h5" sx={{ textAlign: "left" }}>
  289. <FormattedMessage id="transactionRefNo"/>: {transactionData.transactionid}
  290. </Typography>
  291. {/* <Typography variant="h5" sx={{ textAlign: "left" }}>
  292. 付款金額: HK$ {FormatUtils.currencyFormat(props.totalAmount)}
  293. </Typography> */}
  294. {!props.onReady ?
  295. <LoadingComponent />
  296. :availableMethodData.length>0?
  297. <Grid container spacing={2} direction="column" justifyContent="space-between" alignItems="flex-start">
  298. <Grid item xs={12} md={12}>
  299. <Grid container spacing={1} direction="row" justifyContent="flex-start" alignItems="center">
  300. <Grid item>
  301. <Typography variant="h5" sx={{ textAlign: "left" }}>
  302. <FormattedMessage id="selectPaymentMethod"/>:
  303. </Typography>
  304. </Grid>
  305. <Grid item sx={{display: { sm: 'block', md: 'none' }}}></Grid>
  306. <Grid item>
  307. <Button variant="contained" color="white" onClick={selectedPaymentMethodHandle("FPS")} disabled={props.fpsStatus.active !== "Y" || isPaying}>
  308. <img className={fpsClass} src={FpsIcon} width="80" height="80" alt="FPS"></img>
  309. </Button>
  310. </Grid>
  311. <Grid item>
  312. <Button variant="contained" color="white" onClick={selectedPaymentMethodHandle("Visa")} disabled={props.creditCardStatus.active !== "Y" || isPaying}>
  313. <img className={visaClass} src={VisaIcon} width="80" height="80" alt="Visa"></img>
  314. </Button>
  315. </Grid>
  316. <Grid item>
  317. <Button variant="contained" color="white" onClick={selectedPaymentMethodHandle("MasterCard")} disabled={props.creditCardStatus.active !== "Y" || isPaying}>
  318. <img className={mastercardClass} src={MasterIcon} width="80" height="80" alt="MasterCard"></img>
  319. </Button>
  320. </Grid>
  321. <Grid item>
  322. <Button variant="contained" color="white" onClick={selectedPaymentMethodHandle("UnionPay")} disabled={props.unionPayStatus.active !== "Y" || isPaying}>
  323. <img className={unionPayClass} src={UnionPayIcon} width="80" height="80" alt="UnionPay"></img>
  324. </Button>
  325. </Grid>
  326. <Grid item>
  327. <Button variant="contained" color="white" onClick={selectedPaymentMethodHandle("JCB")} disabled={props.creditCardStatus.active !== "Y" || isPaying}>
  328. <img className={jCBClass} src={JcbIcon} width="80" height="80" alt="JCB"></img>
  329. </Button>
  330. </Grid>
  331. <Grid item>
  332. {props.browserType==mobileBrowser?
  333. null
  334. :
  335. <Button variant="contained" color="white" onClick={selectedPaymentMethodHandle("PPS")} disabled={props.ppsStatus.active !== "Y" || isPaying}>
  336. <img className={pPSClass} src={PpsIcon} width="80" height="80" alt="PPS"></img>
  337. </Button>
  338. }
  339. </Grid>
  340. </Grid>
  341. </Grid>
  342. {paymentMethod !=""?
  343. <Grid item xs={12} md={12}>
  344. <Grid container direction="row" justifyContent="flex-start" alignItems="center">
  345. <Grid item>
  346. <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "center" }}>
  347. <FormattedMessage id="selectedPaymentMethod"/>:&nbsp;
  348. </FormLabel>
  349. </Grid>
  350. <Grid item>
  351. <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "center" }}>
  352. {paymentMethod}
  353. </FormLabel>
  354. </Grid>
  355. </Grid>
  356. </Grid>
  357. : null}
  358. </Grid>:
  359. <Grid container direction="row" justifyContent="center" alignItems="center">
  360. <FormLabel sx={{ fontSize: "20px", color: "#000000", textAlign: "center"}}>
  361. <FormattedMessage id="paymentMethodNotAvailable"/>
  362. </FormLabel>
  363. </Grid>
  364. }
  365. </Grid>
  366. <Grid item xs={12} md={12}>
  367. <Grid container >
  368. <Grid item>
  369. <Typography variant="h5" sx={{ color: "#000000", textAlign: "left" }}>
  370. <FormattedMessage id="payTotal"/>&nbsp;($):&nbsp;
  371. </Typography>
  372. </Grid>
  373. <Grid item>
  374. <Typography variant="h5" sx={{color: "#000000", textAlign: "left" }}>
  375. {" HK$ " + FormatUtils.currencyFormat(props.totalAmount)}
  376. </Typography>
  377. </Grid>
  378. </Grid>
  379. {paymentMethod === "" ?
  380. paymentMethod !="" && !onReady ? <LoadingComponent/> : null
  381. :
  382. !isLimit?
  383. !onReady? <LoadingComponent/> :
  384. !isLimit?
  385. filteredPaymentMethod.length>0?
  386. filteredPaymentMethod.map((availableMethod) => {
  387. return (
  388. <Grid container key={availableMethod.subtype} className={"css-1tx0bae"} sx={{ mt: 1, p:2 }}>
  389. {
  390. availableMethod.pointstonote.map((pointstonote) => {
  391. return (
  392. <Grid container key={pointstonote.order} sx={{ p:0.5 }} direction="row" justifyContent="flex-start" alignItems="center">
  393. <Grid item>
  394. <Typography sx={{fontSize: "16px", color: "#000000", textAlign: "left" }}>
  395. {pointstonote.content}
  396. </Typography>
  397. </Grid>
  398. </Grid>
  399. );
  400. })
  401. }
  402. </Grid>
  403. );
  404. })
  405. : null
  406. : null
  407. :
  408. !onReady? <LoadingComponent/> :
  409. <Grid container className={"css-1tx0bae"} sx={{ mt: 1, p:2 }}>
  410. <Grid container sx={{ p:0.5 }} direction="row" justifyContent="flex-start" alignItems="center">
  411. <Grid item>
  412. <Typography color="error" sx={{fontSize: "16px", textAlign: "left" }}>
  413. {filteredPaymentMethod.pointstonote}
  414. </Typography>
  415. </Grid>
  416. </Grid>
  417. </Grid>
  418. }
  419. </Grid>
  420. </center>
  421. </Grid>
  422. </Box>
  423. </Grid>
  424. </DialogContentText>
  425. </DialogContent>
  426. </form>
  427. </FormikProvider>
  428. <Stack direction="row" justifyContent="space-around">
  429. <DialogActions>
  430. <Button variant="contained" onClick={closeHandle()} autoFocus sx={PRIMARY_CONTAINED_BUTTON_SX}>
  431. <FormattedMessage id="cancel"/>
  432. </Button>
  433. </DialogActions>
  434. <DialogActions>
  435. {
  436. props.onPayment || isPaying?
  437. <LoadingComponent disableText={true} alignItems="flex-start"/>
  438. :
  439. <Button variant="contained" onClick={confirmPaymentHandle()} disabled={paymentMethod === "" || isLimit || isPPSLimit || isPaying} sx={PAY_CONTAINED_BUTTON_SX}>
  440. <FormattedMessage id="pay"/>
  441. </Button>
  442. }
  443. </DialogActions>
  444. </Stack>
  445. <div>
  446. <Dialog
  447. open={paymentHoldedErr}
  448. onClose={() => setPaymentHoldedErr(false)}
  449. PaperProps={{
  450. sx: {
  451. minWidth: '40vw',
  452. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  453. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  454. }
  455. }}
  456. >
  457. <DialogTitle></DialogTitle>
  458. <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography>
  459. <DialogContent style={{ display: 'flex', }}>
  460. <Stack direction="column" justifyContent="space-between">
  461. <SafeHtml html={intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText })} />
  462. </Stack>
  463. </DialogContent>
  464. <DialogActions>
  465. <Button onClick={() => setPaymentHoldedErr(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  466. <Typography variant="h5">
  467. <FormattedMessage id="close" />
  468. </Typography></Button>
  469. </DialogActions>
  470. </Dialog>
  471. </div>
  472. </Dialog>
  473. );
  474. };
  475. export default MultiPaymentWindow;