選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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