378 lines
13 KiB
TypeScript
378 lines
13 KiB
TypeScript
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>
|
||
);
|
||
}
|