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

379 lines
14 KiB
TypeScript
Raw Normal View History

2024-04-12 14:42:25 +00:00
import bigDecimal from "js-big-decimal";
2024-03-22 11:47:37 +00:00
import SectionWrapper from "@components/SectionWrapper";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
2023-10-06 11:56:50 +00:00
import {
2024-04-08 12:56:14 +00:00
Box,
Button,
IconButton,
Typography,
useMediaQuery,
useTheme,
2024-02-14 14:22:12 +00:00
} from "@mui/material";
2024-03-22 11:47:37 +00:00
import { activatePromocode } from "@root/api/promocode";
import { sendPayment, sendRSPayment } from "@root/api/wallet";
2024-02-14 14:22:12 +00:00
import rsPayLogo from "@root/assets/bank-logo/rs-pay.png";
2024-03-22 11:47:37 +00:00
import sberpayLogo from "@root/assets/bank-logo/sberpay.png";
import umoneyLogo from "@root/assets/bank-logo/umaney.png";
import bankCardLogo from "@root/assets/bank-logo/bankcard.png";
2024-02-14 14:22:12 +00:00
import InputTextfield from "@root/components/InputTextfield";
2024-03-22 11:47:37 +00:00
import { VerificationStatus } from "@root/model/account";
import { useUserStore } from "@root/stores/user";
2024-02-14 14:22:12 +00:00
import { currencyFormatter } from "@root/utils/currencyFormatter";
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
2024-03-22 11:47:37 +00:00
import { cardShadow } from "@root/utils/theme";
import { enqueueSnackbar } from "notistack";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
2024-03-22 11:47:37 +00:00
import CollapsiblePromocodeField from "./CollapsiblePromocodeField";
import PaymentMethodCard from "./PaymentMethodCard";
import { SorryModal } from "./SorryModal";
2024-03-22 11:47:37 +00:00
import { WarnModal } from "./WarnModal";
import { mutate } from "swr";
import { allTypesOfPurchases, cancelPayCartProcess } from "@root/stores/allTypesOfPurchases";
import { useAutoPay } from "./useAutoPay";
2024-02-14 14:22:12 +00:00
type PaymentMethod = {
2024-04-08 12:56:14 +00:00
label: string;
name: string;
image: string;
unpopular?: boolean;
2024-02-14 14:22:12 +00:00
};
const paymentMethods: PaymentMethod[] = [
2024-04-08 12:56:14 +00:00
{ label: "Банковская карта", name: "bankCard", image: bankCardLogo },
// { label: "Тинькофф", name: "tinkoffBank", image: tinkoffLogo },
// { label: "СБП", name: "sbp", image: spbLogo },
2024-04-08 12:56:14 +00:00
{ label: "SberPay", name: "sberbank", image: sberpayLogo },
// { label: "B2B Сбербанк", name: "b2bSberbank", image: b2bLogo },
2024-04-08 12:56:14 +00:00
{ label: "ЮMoney", name: "yoomoney", image: umoneyLogo },
2024-02-14 14:22:12 +00:00
];
type PaymentMethodType = (typeof paymentMethods)[number]["name"];
2022-11-22 14:43:59 +00:00
export default function Payment() {
2024-04-08 12:56:14 +00:00
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const navigate = useNavigate();
const handleCustomBackNavigation = useHistoryTracker();
const userId = useUserStore((state) => state.userId) || "";
const notEnoughMoneyAmount = allTypesOfPurchases(state => state.notEnoughMoneyAmount);
const siteReadyPayCart = allTypesOfPurchases(state => state.siteReadyPayCart)
const verificationStatus = useUserStore((state) => state.verificationStatus);
const action = allTypesOfPurchases(state => state.action);
const fromDomain = allTypesOfPurchases(state => state.fromDomain);
const backWay = allTypesOfPurchases(state => state.backWay);
const additionalinformation = allTypesOfPurchases(state => state.additionalinformation);
console.log("Payment - значения из Zustand store:");
console.log("action:", action);
console.log("fromDomain:", fromDomain);
console.log("backWay:", backWay);
console.log("additionalinformation:", additionalinformation);
console.log("notEnoughMoneyAmount:", notEnoughMoneyAmount);
2024-04-08 12:56:14 +00:00
const [promocodeField, setPromocodeField] = useState<string>("");
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethodType | null>("");
2024-03-21 16:34:44 +00:00
const [paymentValueField, setPaymentValueField] = useAutoPay();
const [warnModalOpen, setWarnModalOpen] = useState<boolean>(false);
const [sorryModalOpen, setSorryModalOpen] = useState<boolean>(false);
// paymentValue в копейках для API
const paymentValue = Number(
bigDecimal.multiply(parseFloat(paymentValueField || "0"), 100)
2024-04-12 14:42:25 +00:00
);
2024-03-21 16:34:44 +00:00
useEffect(() => {
return () => {
cancelPayCartProcess();
}
}, [])
2024-07-29 04:06:40 +00:00
//https://shub.pena.digital/quizpayment?action=squizpay&dif=9800&data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ODVhNTc4OTgzZWU3N2Y4ZTFlNjNkYyIsImF1ZCI6InBlbmEiLCJpc3MiOiJwZW5hLWF1dGgtc2VydmljZSIsImlhdCI6MTcyMjIyMjgzMywiZXhwIjoxNzI3NDA2ODMzfQ.My1KJWFk034MiMdImQSlzf5p4Sn5Dhboj2VvPQteh59tD_CwXyPtePEyev3thV_58IbOOgJ5cgeBm0JKn7atgMgRMpNQVdeYKtf6HYvVoAqkrMcT1LHgAlEQ0TcaXssFKCQGuiCVltHY3UE-kQv5TeydBpO3U9BDKvMqRqv5-Xo&userid=6685a578983ee77f8e1e63dc
const handlePaymentClick = () => {
// Проверяем оба источника данных
const hasPaymentValue = Number(paymentValueField) > 0;
const hasNotEnoughMoney = notEnoughMoneyAmount > 0;
console.log("handlePaymentClick - проверка:");
console.log("paymentValueField:", paymentValueField);
console.log("Number(paymentValueField):", Number(paymentValueField));
console.log("notEnoughMoneyAmount:", notEnoughMoneyAmount);
console.log("hasPaymentValue:", hasPaymentValue);
console.log("hasNotEnoughMoney:", hasNotEnoughMoney);
if (!hasPaymentValue && !hasNotEnoughMoney) {
2024-04-08 12:56:14 +00:00
enqueueSnackbar("Введите сумму");
return;
}
if (selectedPaymentMethod === "rspay") {
const amountToCheck = hasPaymentValue ? Number(paymentValueField) : Number(bigDecimal.divide(notEnoughMoneyAmount, 100));
if (amountToCheck < 900) {
enqueueSnackbar("Минимальная сумма 900р");
return;
2024-04-08 12:56:14 +00:00
}
if (verificationStatus !== VerificationStatus.VERIFICATED) {
setWarnModalOpen(true);
return;
}
startPayRS()
} else {
startPayCard()
}
}
2024-03-17 09:22:49 +00:00
const startPayRS = async () => {
// Определяем сумму для оплаты
const hasPaymentValue = Number(paymentValueField) > 0;
const amountInRubles = hasPaymentValue ? Number(paymentValueField) : Number(bigDecimal.divide(notEnoughMoneyAmount, 100));
const [sendRSPaymentResponse] = await sendRSPayment(amountInRubles);
2024-03-17 09:22:49 +00:00
if (sendRSPaymentResponse) {
return enqueueSnackbar(sendRSPaymentResponse);
}
2024-03-17 09:22:49 +00:00
enqueueSnackbar(
"Cпасибо за заявку, в течении 24 часов вам будет выставлен счёт для оплаты услуг."
);
2024-03-17 09:22:49 +00:00
navigate("/settings");
}
const startPayCard = async () => {
if (!selectedPaymentMethod) {
enqueueSnackbar("Введите метод оплаты");
return
}
// Определяем сумму для оплаты
const hasPaymentValue = Number(paymentValueField) > 0;
const amountInRubles = hasPaymentValue ? Number(paymentValueField) : Number(bigDecimal.divide(notEnoughMoneyAmount, 100));
const amountInKopecks = Number(bigDecimal.floor(bigDecimal.multiply(amountInRubles, 100)));
console.log("startPayCard - параметры:");
console.log("action:", action);
console.log("fromDomain:", fromDomain);
console.log("backWay:", backWay);
console.log("amountInRubles:", amountInRubles);
console.log("amountInKopecks:", amountInKopecks);
const [sendPaymentResponse, sendPaymentError] = await sendPayment({
userId,
body: {
type: selectedPaymentMethod,
amount: amountInKopecks,
},
action: action || undefined,
fromDomain: fromDomain || undefined,
backWay: backWay || undefined,
additionalinformation: additionalinformation || undefined,
});
2024-03-17 09:22:49 +00:00
if (sendPaymentError) {
return enqueueSnackbar(sendPaymentError);
}
2024-03-17 09:22:49 +00:00
//Произошёл запрос на пополнение счёта. Нам вернули ссылку для перехода на страницу пополнения.
if (sendPaymentResponse) {
document.location.href = sendPaymentResponse.link;
2024-02-14 14:22:12 +00:00
}
2024-04-08 12:56:14 +00:00
}
2024-02-14 14:22:12 +00:00
2024-05-28 13:38:01 +00:00
async function handleApplyPromocode() {
2024-04-08 12:56:14 +00:00
if (!promocodeField) return;
2024-02-14 14:22:12 +00:00
2024-05-28 13:38:01 +00:00
const [greeting, activateError] = await activatePromocode(promocodeField);
if (activateError) {
return enqueueSnackbar(activateError);
}
enqueueSnackbar(greeting);
mutate("discounts");
2024-04-08 12:56:14 +00:00
}
2024-02-14 14:22:12 +00:00
2024-04-08 12:56:14 +00:00
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",
}}
>
2024-05-09 09:36:47 +00:00
{window.history.length > 1 && (
<IconButton
onClick={handleCustomBackNavigation}
sx={{ p: 0, height: "28px", width: "28px", color: "black" }}
>
<ArrowBackIcon />
</IconButton>
)}
2024-04-08 12:56:14 +00:00
<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
2024-02-14 14:22:12 +00:00
sx={{
2024-04-08 12:56:14 +00:00
width: "100%",
display: "flex",
flexDirection: upSm ? "row" : "column",
flexWrap: "wrap",
gap: upMd ? "14px" : "20px",
alignContent: "start",
2024-02-14 14:22:12 +00:00
}}
2024-04-08 12:56:14 +00:00
>
{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,
}}
2024-03-21 16:34:44 +00:00
>
2024-04-08 12:56:14 +00:00
<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(notEnoughMoneyAmount, 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" }}
/>
}
2024-04-08 12:56:14 +00:00
</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>
2024-04-08 12:56:14 +00:00
</Box>
</Box>
<WarnModal open={warnModalOpen} setOpen={setWarnModalOpen} />
<SorryModal open={sorryModalOpen} setOpen={setSorryModalOpen} />
</SectionWrapper>
);
}
2025-07-08 03:04:33 +00:00
//https://shub.pena.digital/quizpayment?action=squizpay&dif=50000&data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4NDcxNjAxYzE0OWU0ZDI0ZWJmNTExNyIsImF1ZCI6InBlbmEiLCJpc3MiOiJwZW5hLWF1dGgtc2VydmljZSIsImlhdCI6MTc0OTQ4OTE1MywiZXhwIjoxNzU0NjczMTUzfQ.SqEXFSPzP3ugIscqLywGkjFJmFqx13zOtxGAjZQ6SzUw8w9ZXjE9uFn8VbLBMGPqeJbvcT2jRV2bB5qtqMy1T3aNuSZ9AZW0jY1hGvFB-bSrYguMV1yErLkR45SvdK2jGI6dg3p6LRqCHb2JMl8vur_KKaus3GiJlsTNNR0fhgI&userid=68471601c149e4d24ebf5117&from=AI&wayback=ai_27969