front-hub/src/pages/Payment/Payment.tsx

378 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import bigDecimal from "js-big-decimal";
import SectionWrapper from "@components/SectionWrapper";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import {
Box,
Button,
IconButton,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { activatePromocode } from "@root/api/promocode";
import { sendPayment, sendRSPayment } from "@root/api/wallet";
import b2bLogo from "@root/assets/bank-logo/b2b.png";
import tinkoffLogo from "@root/assets/bank-logo/logo-tinkoff.png";
import rsPayLogo from "@root/assets/bank-logo/rs-pay.png";
import sberpayLogo from "@root/assets/bank-logo/sberpay.png";
import spbLogo from "@root/assets/bank-logo/spb.png";
import umoneyLogo from "@root/assets/bank-logo/umaney.png";
import bankCardLogo from "@root/assets/bank-logo/bankcard.png";
import InputTextfield from "@root/components/InputTextfield";
import { VerificationStatus } from "@root/model/account";
import { useUserStore } from "@root/stores/user";
import { currencyFormatter } from "@root/utils/currencyFormatter";
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
import { cardShadow } from "@root/utils/theme";
import { enqueueSnackbar } from "notistack";
import { useEffect, useLayoutEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import CollapsiblePromocodeField from "./CollapsiblePromocodeField";
import PaymentMethodCard from "./PaymentMethodCard";
import { SorryModal } from "./SorryModal";
import { WarnModal } from "./WarnModal";
import { mutate } from "swr";
import { calcTimeOfReadyPayCart, cancelPayCartProcess, setNotEnoughMoneyAmount, startPayCartProcess, useNotEnoughMoneyAmount } from "@root/stores/notEnoughMoneyAmount";
type PaymentMethod = {
label: string;
name: string;
image: string;
unpopular?: boolean;
};
const paymentMethods: PaymentMethod[] = [
{ label: "Банковская карта", name: "bankCard", image: bankCardLogo },
// { label: "Тинькофф", name: "tinkoffBank", image: tinkoffLogo },
// { label: "СБП", name: "sbp", image: spbLogo },
{ label: "SberPay", name: "sberbank", image: sberpayLogo },
// { label: "B2B Сбербанк", name: "b2bSberbank", image: b2bLogo },
{ label: "ЮMoney", name: "yoomoney", image: umoneyLogo },
];
type PaymentMethodType = (typeof paymentMethods)[number]["name"];
export default function Payment() {
const theme = useTheme();
const location = useLocation();
const navigate = useNavigate();
const handleCustomBackNavigation = useHistoryTracker();
const userId = useUserStore((state) => state.userId) || "";
const verificationStatus = useUserStore((state) => state.verificationStatus);
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const [promocodeField, setPromocodeField] = useState<string>("");
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethodType | null>("");
const [paymentValueField, setPaymentValueField] = useState<string>("0");
const [fromSquiz, setIsFromSquiz] = useState<boolean>(false);
console.log("fromSquiz")
console.log(fromSquiz)
const [warnModalOpen, setWarnModalOpen] = useState<boolean>(false);
const [sorryModalOpen, setSorryModalOpen] = useState<boolean>(false);
const notEnoughMoneyAmount = useNotEnoughMoneyAmount(state => state.notEnoughMoneyAmount)
const siteReadyPayCart = useNotEnoughMoneyAmount(state => state.siteReadyPayCart)
const paymentValue = parseFloat(
bigDecimal.multiply(parseFloat(paymentValueField), 100)
);
//Отмена состояния сайта "в процессе покупки корзины, просто нехватило деняк"
useEffect(() => {
return () => {
//При выходе со страницы оплаты мы точно не в состоянии покупки корзины
cancelPayCartProcess()
}
}, [])
console.log(siteReadyPayCart)
console.log(notEnoughMoneyAmount)
//Тут записываем начальную сумму в инпут (если стоит флаг что мы в процессе покупки)
useEffect(() => {
console.log(siteReadyPayCart)
console.log(notEnoughMoneyAmount)
if (siteReadyPayCart !== null && siteReadyPayCart[userId] !== undefined && calcTimeOfReadyPayCart(siteReadyPayCart[userId]))
setPaymentValueField((notEnoughMoneyAmount / 100).toString());//Сколько нехватило на хабе
}, [notEnoughMoneyAmount, siteReadyPayCart])
useLayoutEffect(() => {
//Предустановленное значение - это либо 0, либо сколько нам нехватило на хабе, либо сколько нам нехватило на квизе
const params = new URLSearchParams(window.location.search);
const fromSquiz = params.get("action");
const userid = params.get("user");
console.log(fromSquiz)
console.log(fromSquiz === "squizpay")
console.log(userid)
console.log(userid !== null)
if (fromSquiz === "squizpay" && userid !== null) {
setIsFromSquiz(true);
}
//Принимаем параметры из странички и очищаем url адрес до красивого состояния
navigate(`/payment`, {
replace: true,
});
}, []);
//https://shub.pena.digital/quizpayment?action=squizpay&dif=9800&data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ODVhNTc4OTgzZWU3N2Y4ZTFlNjNkYyIsImF1ZCI6InBlbmEiLCJpc3MiOiJwZW5hLWF1dGgtc2VydmljZSIsImlhdCI6MTcyMjIyMjgzMywiZXhwIjoxNzI3NDA2ODMzfQ.My1KJWFk034MiMdImQSlzf5p4Sn5Dhboj2VvPQteh59tD_CwXyPtePEyev3thV_58IbOOgJ5cgeBm0JKn7atgMgRMpNQVdeYKtf6HYvVoAqkrMcT1LHgAlEQ0TcaXssFKCQGuiCVltHY3UE-kQv5TeydBpO3U9BDKvMqRqv5-Xo&userid=6685a578983ee77f8e1e63dc
const handlePaymentClick = () => {
if (Number(paymentValueField) === 0) {
enqueueSnackbar("Введите сумму");
return;
}
if (selectedPaymentMethod === "rspay") {
if (Number(paymentValueField) < 900) {
enqueueSnackbar("Минимальная сумма 900р");
return;
}
if (verificationStatus !== VerificationStatus.VERIFICATED) {
setWarnModalOpen(true);
return;
}
startPayRS()
} else {
startPayCard()
}
}
const startPayRS = async () => {
const [sendRSPaymentResponse] = await sendRSPayment(
Number(paymentValueField)
);
if (sendRSPaymentResponse) {
return enqueueSnackbar(sendRSPaymentResponse);
}
enqueueSnackbar(
"Cпасибо за заявку, в течении 24 часов вам будет выставлен счёт для оплаты услуг."
);
navigate("/settings");
}
const startPayCard = async () => {
if (!selectedPaymentMethod) {
enqueueSnackbar("Введите метод оплаты");
return
}
const [sendPaymentResponse, sendPaymentError] = await sendPayment({
userId: userId ?? "",
fromSquiz,
body: {
type: selectedPaymentMethod,
amount: Number(
bigDecimal.floor(
bigDecimal.multiply(Number(paymentValueField), 100)
)
),
},
paymentPurpose: notEnoughMoneyAmount ? "paycart" : "replenishwallet",
});
if (sendPaymentError) {
return enqueueSnackbar(sendPaymentError);
}
//Произошёл запрос на пополнение счёта. Нам вернули ссылку для перехода на страницу пополнения.
if (sendPaymentResponse) {
document.location.href = sendPaymentResponse.link;
}
}
async function handleApplyPromocode() {
if (!promocodeField) return;
const [greeting, activateError] = await activatePromocode(promocodeField);
if (activateError) {
return enqueueSnackbar(activateError);
}
enqueueSnackbar(greeting);
mutate("discounts");
}
return (
<SectionWrapper
maxWidth="lg"
sx={{
mt: "25px",
mb: "70px",
px: isTablet ? (upMd ? "40px" : "18px") : "20px",
}}
>
<Box
sx={{
mt: "20px",
mb: "40px",
display: "flex",
gap: "10px",
}}
>
{window.history.length > 1 && (
<IconButton
onClick={handleCustomBackNavigation}
sx={{ p: 0, height: "28px", width: "28px", color: "black" }}
>
<ArrowBackIcon />
</IconButton>
)}
<Typography variant="h4">Способ оплаты</Typography>
</Box>
{!upMd && (
<Typography variant="body2" mb="30px">
Выберите способ оплаты
</Typography>
)}
<Box
sx={{
backgroundColor: upMd ? "white" : undefined,
display: "flex",
flexDirection: upMd ? "row" : "column",
borderRadius: "12px",
boxShadow: upMd ? cardShadow : undefined,
}}
>
<Box
sx={{
width: upMd ? "68.5%" : undefined,
p: upMd ? "20px" : undefined,
display: "flex",
flexDirection: "column",
alignItems: "start",
gap: "40px",
}}
>
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: upSm ? "row" : "column",
flexWrap: "wrap",
gap: upMd ? "14px" : "20px",
alignContent: "start",
}}
>
{paymentMethods.map(({ name, label, image, unpopular = false }) => (
<PaymentMethodCard
isSelected={selectedPaymentMethod === name}
key={name}
label={label}
image={image}
onClick={() => {
setSelectedPaymentMethod(name);
}}
unpopular={false}
/>
))}
<PaymentMethodCard
isSelected={selectedPaymentMethod === "rspay"}
label={"Расчётный счёт"}
image={rsPayLogo}
onClick={async () => {
setSelectedPaymentMethod("rspay");
}}
unpopular={false}
/>
</Box>
<CollapsiblePromocodeField
fieldValue={promocodeField}
onFieldChange={setPromocodeField}
onPromocodeApply={handleApplyPromocode}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "start",
color: theme.palette.gray.dark,
width: upMd ? "31.5%" : undefined,
p: upMd ? "20px" : undefined,
pl: upMd ? "33px" : undefined,
mt: upMd ? undefined : "30px",
borderLeft: upMd
? `1px solid ${theme.palette.gray.main}`
: undefined,
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
maxWidth: "85%",
}}
>
{upMd && <Typography mb="56px">Выберите способ оплаты</Typography>}
<Typography mb="20px">К оплате</Typography>
{
siteReadyPayCart?.[userId] && notEnoughMoneyAmount > 0 ?
<Typography
sx={{
fontWeight: 500,
fontSize: "20px",
lineHeight: "48px",
mb: "28px",
}}
>
{currencyFormatter.format(
Number(bigDecimal.divide(bigDecimal.floor(paymentValue), 100))
)}
</Typography>
:
<InputTextfield
TextfieldProps={{
placeholder: "К оплате",
value: paymentValueField,
type: "number",
}}
onChange={(e) => {
const value = parseFloat(
e.target.value.replace(/^0+(?=\d\.)/, "")
);
setPaymentValueField(isNaN(value) ? "" : value.toString());
}}
id="payment-amount"
gap={upMd ? "16px" : "10px"}
color={"#F2F3F7"}
FormInputSx={{ mb: "28px" }}
/>
}
</Box>
<Button
variant="pena-outlined-light"
disabled={!isFinite(paymentValue)}
onClick={handlePaymentClick}
sx={{
mt: "auto",
color: "black",
border: `1px solid ${theme.palette.purple.main}`,
"&:hover": {
color: "white",
},
"&:active": {
color: "white",
},
}}
>
Оплатить
</Button>
</Box>
</Box>
<WarnModal open={warnModalOpen} setOpen={setWarnModalOpen} />
<SorryModal open={sorryModalOpen} setOpen={setSorryModalOpen} />
</SectionWrapper>
);
}