Anna Ho 1 anno fa
parent
commit
de1fc5cd6d
8 ha cambiato i file con 32407 aggiunte e 28524 eliminazioni
  1. +30309
    -28170
      package-lock.json
  2. +0
    -249
      src/pages/Payment/Card/card.js
  3. +234
    -3
      src/pages/Payment/Card/index.js
  4. +1
    -1
      src/pages/Payment/FPS/FPS.js
  5. +98
    -0
      src/pages/Payment/PaymentSuccess.js
  6. +11
    -3
      src/pages/Payment/index.js
  7. +5
    -0
      src/routes/PublicUserRoutes.js
  8. +1749
    -98
      yarn.lock

+ 30309
- 28170
package-lock.json
File diff soppresso perché troppo grande
Vedi File


+ 0
- 249
src/pages/Payment/Card/card.js Vedi File

@@ -1,249 +0,0 @@
// material-ui
import {
Grid,
Typography,
Stack,
Button,
} from '@mui/material';
import * as React from "react";
import * as HttpUtils from "utils/HttpUtils";
import { useNavigate } from "react-router-dom";
import FpsIcon from "assets/images/icons/fps.svg";
import { useLocation } from 'react-router-dom';
import {paymentPath} from "auth/utils";
// import {poll} from "utils/Utils";

import Loadable from 'components/Loadable';
const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));

import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
height: '100%',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundColor: '#0C489E',
backgroundPosition: 'right'
}

// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
const navigate = useNavigate()
const location = useLocation();

const [paymentData, setPaymentData] = React.useState({});
const [onReady, setOnReady] = React.useState(false);
const [paymentid, setPaymentid] = React.useState("");
const [fpsmerchanttimeoutdatetime, setFpsmerchanttimeoutdatetime] = React.useState("");
const [fpsqrcodeimgbase64, setFpsqrcodeimgbase64] = React.useState("");
const [fpsqrcodeurl, setFpsqrcodeurl] = React.useState("");

const loadPaymentUrl = "/api/payment/wallet/fps";
const cancelPaymentUrl = "/api/payment/cancelpayment";
const paymentStatusApi = "/api/payment/status/";
// const payloadUrl = "/api/payment/wallet/fps/enquiryfpspayload/";
// const receiverUrl = "/noti-api/payment/payment-notification";

React.useEffect(() => {
setFpsqrcodeimgbase64("")
if(location.state != undefined){
setPaymentData(location.state)
}
loadForm();
}, []);

React.useEffect(() => {
setOnReady(true);
}, [fpsqrcodeimgbase64]);


const loadForm = () => {
HttpUtils.post({
url: paymentPath+loadPaymentUrl,
params:{
"transactionid": paymentData.transactionid,
"webtoken": paymentData.webtoken,
"paymentmethod":"04,BCFP,FPS",
"order": {
"totalamount":paymentData.amount,
"currency":"HKD",
"orderdetail":
[
{
"itemid": "1",
"qty":"1",
"unitprice":paymentData.amount,
"amount":paymentData.amount
},
]
},
// "locale":"<locale>",
// "eserviceid":"<eserviceid>"
},
onSuccess: function(responseData){
/*
{
"paymentid": "<paymentid>",
"paymentstatus": "<paymentstatus>",
"fpsmerchanttimeoutdatetime": <fpsmerchanttimeoutdatetime>,
"fpsqrcodeimgbase64": "<fpsqrcodeimgbase64>",
"fpsqrcodeurl": "<fpsqrcodeurl>"
}
*/
setFpsqrcodeimgbase64(responseData.fpsqrcodeimgbase64);
setPaymentid(responseData.paymentid);
setFpsmerchanttimeoutdatetime(responseData.fpsmerchanttimeoutdatetime);
const parsedUrl = new URL(responseData.fpsqrcodeurl);
const fpsqrcodeurl = parsedUrl.pathname
setFpsqrcodeurl(fpsqrcodeurl)
polling()
}
});
}

const getPaymentStatus = () => {
HttpUtils.post({
url: paymentPath+paymentStatusApi+paymentid,
params:{
"apprefid": paymentData.transactionid,
"webtoken": paymentData.webtoken,
},
onSuccess: function(responseData){
const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode;
if (paymentstatuscode === 'APPR') {
const timestamp = '?t=' + Date.now();
window.top.location.href = payment.config.domain + payment.config.ackPagePath + timestamp;
} else if (paymentstatuscode === 'CANC') {
window.top.location.href = payment.config.domain + payment.config.indexPagePath;
} else {
window.top.location.href = payment.config.domain + payment.config.errPagePath;
}
},
onError: function(){
cancelPayment()
}
});
};

React.useEffect(() => {

//Fake
setFpsmerchanttimeoutdatetime(new Date().now()?500:null);
//
const timeOut = fpsmerchanttimeoutdatetime;
const interval = setInterval(() => {
getPaymentStatus();
console.log("test");
}, timeOut);
return () => clearInterval(interval);
}, []);

const cancelPayment = ()=>{
HttpUtils.post({
url: paymentPath+cancelPaymentUrl,
params:{
"transactionid": paymentData.transactionid,
"webtoken": paymentData.webtoken,
"paymentid": paymentid
},
onSuccess: function(){
navigate("/dashboard");
}
});
}

// const getPayload = ()=>{
// HttpUtils.get({
// url: payloadUrl+"<paymenttoken>",
// onSuccess: function(responseData){
// /*
// {
// "payload": "<payload>"
// }

// */
// }
// });
// }

// const getReceiver = ()=>{
// HttpUtils.get({
// url: receiverUrl+"<paymenttoken>",
// onSuccess: function(responseData){
// /*
// {
// "payload": "<payload>"
// }

// */
// }
// });
// }


return (
!onReady ?
<LoadingComponent />
:
(
<Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" >
<Grid item xs={12} width="100%">
<div style={BackgroundHead} width="100%">
<Stack direction="row" height='70px'>
<Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>公共啟事:FPS付款</Typography>
</Stack>
</div>
</Grid>
{/*row 1*/}
<Grid item xs={12} md={12} >
<Grid container justifyContent="flex-start" alignItems="center" >
<center>
<Grid item xs={12} md={12} >

<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
<img src={FpsIcon} width="80" height="80" alt="FPS"></img>
<br />
支付金額
<br />
{"$HK " + paymentData.amount}
</Typography>

<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
請掃描以下二維碼
<br />
<img src={fpsqrcodeimgbase64} alt="QR Code"/>
<img src={fpsqrcodeurl} alt="QR Code"/>
<br />
二維碼有效期限10分鐘
<br />
請在規定時間內完成付款流程
</Typography>

<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
<Button
component="span"
variant="contained"
size="large"
color="error"
onClick={()=>{
cancelPayment();
}}
sx={{ m: 4 }}
>取消付款</Button>
</Typography>
</Grid>
</center>
</Grid>
</Grid>
{/*row 2*/}
</Grid >
)


);
};

export default Index;

+ 234
- 3
src/pages/Payment/Card/index.js Vedi File

@@ -1,11 +1,242 @@
// material-ui
import {
Grid,
Typography,
Stack,
Button,
} from '@mui/material';
import * as React from "react";
import * as HttpUtils from "utils/HttpUtils";
import { useNavigate } from "react-router-dom";
import FpsIcon from "assets/images/icons/fps.svg";
import { useLocation } from 'react-router-dom';
import {paymentPath} from "auth/utils";
// import {poll} from "utils/Utils";

import Loadable from 'components/Loadable';
const Card = Loadable(React.lazy(() => import('./card')));
const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));

import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
height: '100%',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundColor: '#0C489E',
backgroundPosition: 'right'
}


const Index = ({ isPopUp, url }) => {

const navigate = useNavigate()
const location = useLocation();

const [paymentData, setPaymentData] = React.useState({});
const [onReady, setOnReady] = React.useState(false);
const [paymentid, setPaymentid] = React.useState("");
const [fpsmerchanttimeoutdatetime, setFpsmerchanttimeoutdatetime] = React.useState("");
const [redirecturl, setRedirectUrl] = React.useState("");
// const [paymentstatuscode, setPaymentstatuscode] = React.useState("");
// const [fpsqrcodeurl, setFpsqrcodeurl] = React.useState("");
// const pasgPath = 'https://fps.payapps.hkicl.com.hk'; //PRD
// const pasgPath = 'https://sim.fps.payapps.hkicl.com.hk'; //Testing
// const loadPaymentUrl = "/api/payment/web/";
const cancelPaymentUrl = "/api/payment/cancelpayment";
// const paymentStatusApi = "/api/payment/status/";
// const payloadUrl = "/api/payment/wallet/fps/enquiryfpspayload/";
// const receiverUrl = "/noti-api/payment/payment-notification";

//timer
const currentTimer = React.useRef();
const [time, setTime] = React.useState(0);

React.useEffect(() => {
setFpsqrcodeimgbase64("")
if (location.state != undefined) {
setPaymentData(location.state)
}
loadForm();
currentTimer.current = setInterval(() => {
getPaymentStatus();
setTime((prevTime) => prevTime + 1);
}, 500);
return () => clearInterval(currentTimer.current);
}, []);

React.useEffect(() => {
setOnReady(true);
}, [redirecturl]);


const loadForm = () => {
const timeoutdatetime = "2023-10-26T09:04:30Z[UTC]"
const convertedDateString = timeoutdatetime.replace("[UTC]", "");
setFpsmerchanttimeoutdatetime(convertedDateString)
setPaymentid("C202310268000681")

// HttpUtils.post({
// url: paymentPath+loadPaymentUrl+paymentData.type,
// params:{
// "transactionid": paymentData.transactionid,
// "apprefid:": paymentData.transactionid,
// "webtoken": paymentData.webtoken,
// "paymentmethod":paymentData.paymentMethod,
// "order": {
// "totalamount":paymentData.amount,
// "currency":"HKD",
// "orderdetail":
// [
// {
// "itemid": "1",
// "qty":"1",
// "unitprice":paymentData.amount,
// "amount":paymentData.amount
// },
// ]
// },
// // "locale":"<locale>",
// // "eserviceid":"<eserviceid>",
// "returnurl": window.location.hostname+"/payment/success"
// },
// onSuccess: function(responseData){
// /*
// {
// "transactionid": "<transactionid>",
// "redirecturl": "<redirecturl>"
// }
// */
// setRedirectUrl(responseData.redirecturl);
//
// }
// });
}

const getPaymentStatus = () => {

// HttpUtils.post({
// url: paymentPath+paymentStatusApi+paymentid,
// params:{
// "apprefid": paymentData.transactionid,
// "webtoken": paymentData.webtoken,
// },
// onSuccess: function(responseData){
// const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode;
// if (paymentstatuscode === 'APPR') {
// const timestamp = '?t=' + Date.now();
// window.top.location.href = paymentPath + payment.config.ackPagePath + timestamp;
// } else if (paymentstatuscode === 'CANC') {
// window.top.location.href = paymentPath + payment.config.indexPagePath;
// } else {
// window.top.location.href = paymentPath + payment.config.errPagePath;
// }
// },
// onError: function(){
// cancelPayment()
// clearInterval(currentTimer.current);
// }
// });
};

React.useEffect(() => {
const timeOutDate = new Date(fpsmerchanttimeoutdatetime);
const currentTime = new Date;
if (timeOutDate.getTime() < currentTime.getTime()) {
console.log("stop");
clearInterval(currentTimer.current);
}
}, [time])

const cancelPayment = () => {
HttpUtils.post({
url: paymentPath + cancelPaymentUrl,
params: {
"transactionid": paymentData.transactionid,
"webtoken": paymentData.webtoken,
"paymentid": paymentid
},
onSuccess: function () {
navigate("/dashboard");
}
});
}

// const getPayload = ()=>{
// HttpUtils.get({
// url: payloadUrl+"<paymenttoken>",
// onSuccess: function(responseData){
// /*
// {
// "payload": "<payload>"
// }

// */
// }
// });
// }

// const getReceiver = ()=>{
// HttpUtils.get({
// url: receiverUrl+"<paymenttoken>",
// onSuccess: function(responseData){
// /*
// {
// "payload": "<payload>"
// }

// */
// }
// });
// }

const Index = () => {
return (
<Card/>
!onReady ?
<LoadingComponent />
:
(
<Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" >
<Grid item xs={12} width="100%">
<div style={BackgroundHead} width="100%">
<Stack direction="row" height='70px'>
<Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>公共啟事: 信用卡付款</Typography>
</Stack>
</div>
</Grid>
{/*row 1*/}
<Grid item xs={12} md={12} >
<Grid container justifyContent="flex-start" alignItems="center" >
<center>
<Grid item xs={12} md={12} >

<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
<img src={FpsIcon} width="80" height="80" alt="FPS"></img>
<br />
支付金額
<br />
{"$HK " + paymentData.amount}
</Typography>


<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
<Button
component="span"
variant="contained"
size="large"
color="error"
onClick={() => {
cancelPayment();
}}
sx={{ m: 4 }}
>取消付款</Button>
</Typography>
</Grid>
</center>
</Grid>
</Grid>
{/*row 2*/}
</Grid >
)
);
}


+ 1
- 1
src/pages/Payment/FPS/FPS.js Vedi File

@@ -233,7 +233,7 @@ const Index = () => {
<br />
二維碼有效期限10分鐘
<br />
請在規定時間內完成付款流程 ({fpsmerchanttimeoutdatetime})
請在規定時間內完成付款流程
</Typography>

<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>


+ 98
- 0
src/pages/Payment/PaymentSuccess.js Vedi File

@@ -0,0 +1,98 @@
// material-ui
import {
Grid,
Typography,
Stack,
} from '@mui/material';
import * as UrlUtils from "utils/ApiPathConst";
import * as React from "react";
import * as HttpUtils from "utils/HttpUtils";
import { useParams } from "react-router-dom";
import { useNavigate } from "react-router-dom";

import Loadable from 'components/Loadable';
const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));

import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
height: '100%',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundColor: '#0C489E',
backgroundPosition: 'right'
}

// ==============================|| DASHBOARD - DEFAULT ||============================== //

const Index = () => {
const params = useParams();
const navigate = useNavigate()

const [record, setRecord] = React.useState();
const [onReady, setOnReady] = React.useState(false);

React.useEffect(() => {
loadForm();
}, []);

React.useEffect(() => {
setOnReady(true);
}, [record]);


const loadForm = () => {
if (params.id > 0) {
HttpUtils.get({
url: UrlUtils.GET_PROOF_PAY + "/" + params.id,
onSuccess: (responseData) => {
if (!responseData.data?.id) {
navigate("/proof/search");
}
setRecord(responseData.data);
}
});
}
}

return (
!onReady ?
<LoadingComponent />
:
(
<Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" >
<Grid item xs={12} width="100%">
<div style={BackgroundHead} width="100%">
<Stack direction="row" height='70px'>
<Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>公共啟事: 付款</Typography>
</Stack>
</div>
</Grid>
{/*row 1*/}
<Grid item xs={12} md={12} >
<Grid container justifyContent="flex-start" alignItems="center" >
<center>
<Grid item xs={12} md={8} >
<Typography variant="h2" sx={{ textAlign: "left", ml: 4, mr: 4, mt: 4, borderBottom: "1px solid black" }}>
公共啟事:完成付款
</Typography>

<Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}>
付款成功
</Typography>
</Grid>
</center>
</Grid>
</Grid>
{/*row 2*/}
</Grid >


)


);
};

export default Index;

+ 11
- 3
src/pages/Payment/index.js Vedi File

@@ -1,8 +1,7 @@
import * as React from "react";
import { lazy } from 'react';
import * as HttpUtils from "utils/HttpUtils";
import Loadable from 'components/Loadable';
const MultiPaymentWindow = Loadable(lazy(() => import('./MultiPaymentWindow')));
const MultiPaymentWindow = Loadable(React.lazy(() => import('./MultiPaymentWindow')));
// const FPS = Loadable(React.lazy(() => import('./FPS')));
import {useEffect, useState} from "react";
import {useNavigate} from "react-router-dom";
@@ -73,9 +72,18 @@ const Index = () => {
useEffect(() => {
if (confirmPayment){
setOpen(false);
let transactionid = "";
let webtoken = "";

if(selectedPaymentMethod === "FPS"){
navigate('/payment/fps', {state:{amount:totalAmount,transactionid:"",webtoken:""} });
navigate('/payment/fps', {state:{amount:totalAmount,transactionid:transactionid,webtoken:webtoken} });
// navigate('/payment/fps', {state:{amount:totalAmount,transactionid:transactionData.transactionid,webtoken:transactionData.webtoken} });
}else if(selectedPaymentMethod ==="Visa" || selectedPaymentMethod ==="Mastercard"){
navigate('/payment/card', {state:{amount:totalAmount,transactionid:transactionid,webtoken:webtoken, type:"creditcard", paymentMethod:"02,BCMP,CreditCard"} });
}else if(selectedPaymentMethod ==="UnionPay" || selectedPaymentMethod ==="JCB"){
navigate('/payment/card', {state:{amount:totalAmount,transactionid:transactionid,webtoken:webtoken, type:"creditcard", paymentMethod:"03,BCMP,CreditCard"} });
}else if(selectedPaymentMethod ==="PPS"){
navigate('/payment/card', {state:{amount:totalAmount,transactionid:transactionid,webtoken:webtoken, type:"pps", paymentMethod:"01,PPSB,PPS"} });
}
}


+ 5
- 0
src/routes/PublicUserRoutes.js Vedi File

@@ -17,6 +17,7 @@ const ProofPayment = Loadable(lazy(() => import('pages/Proof/Payment')));
const Payment_Multi = Loadable(lazy(() => import('pages/Payment')));
const Payment_FPS = Loadable(lazy(() => import('pages/Payment/FPS')));
const Payment_Card = Loadable(lazy(() => import('pages/Payment/Card')));
const Payment_Success = Loadable(lazy(() => import('pages/Payment/PaymentSuccess')));

// ==============================|| MAIN ROUTING ||============================== //

@@ -75,6 +76,10 @@ const PublicDashboard = {
path: 'payment/card',
element: <Payment_Card/>
},
{
path: 'payment/success',
element: <Payment_Success/>
},
]
},
]


+ 1749
- 98
yarn.lock
File diff soppresso perché troppo grande
Vedi File


Caricamento…
Annulla
Salva