front-hub/src/pages/Payment/Payment.tsx
Nastya 291d059654
Some checks failed
Deploy / CreateImage (push) Failing after 1m0s
Deploy / DeployService (push) Has been skipped
новая система покупок
2025-07-20 13:39:47 +03:00

379 lines
14 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 rsPayLogo from "@root/assets/bank-logo/rs-pay.png";
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";
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, useState } from "react";
import { 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 { allTypesOfPurchases, cancelPayCartProcess } from "@root/stores/allTypesOfPurchases";
import { useAutoPay } from "./useAutoPay";
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 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);
const [promocodeField, setPromocodeField] = useState<string>("");
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethodType | null>("");
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)
);
useEffect(() => {
return () => {
cancelPayCartProcess();
}
}, [])
//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) {
enqueueSnackbar("Введите сумму");
return;
}
if (selectedPaymentMethod === "rspay") {
const amountToCheck = hasPaymentValue ? Number(paymentValueField) : Number(bigDecimal.divide(notEnoughMoneyAmount, 100));
if (amountToCheck < 900) {
enqueueSnackbar("Минимальная сумма 900р");
return;
}
if (verificationStatus !== VerificationStatus.VERIFICATED) {
setWarnModalOpen(true);
return;
}
startPayRS()
} else {
startPayCard()
}
}
const startPayRS = async () => {
// Определяем сумму для оплаты
const hasPaymentValue = Number(paymentValueField) > 0;
const amountInRubles = hasPaymentValue ? Number(paymentValueField) : Number(bigDecimal.divide(notEnoughMoneyAmount, 100));
const [sendRSPaymentResponse] = await sendRSPayment(amountInRubles);
if (sendRSPaymentResponse) {
return enqueueSnackbar(sendRSPaymentResponse);
}
enqueueSnackbar(
"Cпасибо за заявку, в течении 24 часов вам будет выставлен счёт для оплаты услуг."
);
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,
});
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(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" }}
/>
}
</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>
);
}
//https://shub.pena.digital/quizpayment?action=squizpay&dif=50000&data=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4NDcxNjAxYzE0OWU0ZDI0ZWJmNTExNyIsImF1ZCI6InBlbmEiLCJpc3MiOiJwZW5hLWF1dGgtc2VydmljZSIsImlhdCI6MTc0OTQ4OTE1MywiZXhwIjoxNzU0NjczMTUzfQ.SqEXFSPzP3ugIscqLywGkjFJmFqx13zOtxGAjZQ6SzUw8w9ZXjE9uFn8VbLBMGPqeJbvcT2jRV2bB5qtqMy1T3aNuSZ9AZW0jY1hGvFB-bSrYguMV1yErLkR45SvdK2jGI6dg3p6LRqCHb2JMl8vur_KKaus3GiJlsTNNR0fhgI&userid=68471601c149e4d24ebf5117&from=AI&wayback=ai_27969