fix payment
This commit is contained in:
parent
e44c64165a
commit
6169e03d9a
@ -71,6 +71,10 @@ const App = () => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const userId = useUserStore(state => state.userId);
|
const userId = useUserStore(state => state.userId);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Используем обновленный хук useReauthorization для обработки авторизации
|
||||||
|
const { isProcessing } = useReauthorization();
|
||||||
|
|
||||||
useUserFetcher({
|
useUserFetcher({
|
||||||
url: process.env.REACT_APP_DOMAIN + `/user/${userId}`,
|
url: process.env.REACT_APP_DOMAIN + `/user/${userId}`,
|
||||||
userId,
|
userId,
|
||||||
@ -188,6 +192,7 @@ const App = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path={"/image/:srcImage"} element={<ChatImageNewWindow />} />
|
<Route path={"/image/:srcImage"} element={<ChatImageNewWindow />} />
|
||||||
|
|
||||||
<Route element={<PrivateRoute />}>
|
<Route element={<PrivateRoute />}>
|
||||||
<Route element={<ProtectedLayout />}>
|
<Route element={<ProtectedLayout />}>
|
||||||
<Route path="/tariffs" element={<Tariffs />} />
|
<Route path="/tariffs" element={<Tariffs />} />
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
CircularProgress,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { activatePromocode } from "@root/api/promocode";
|
import { activatePromocode } from "@root/api/promocode";
|
||||||
import { sendPayment, sendRSPayment } from "@root/api/wallet";
|
import { sendPayment, sendRSPayment } from "@root/api/wallet";
|
||||||
@ -31,6 +32,7 @@ import { WarnModal } from "./WarnModal";
|
|||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
import { allTypesOfPurchases } from "@root/stores/allTypesOfPurchases";
|
import { allTypesOfPurchases } from "@root/stores/allTypesOfPurchases";
|
||||||
import { useAutoPay } from "@root/utils/hooks/useAutoPay";
|
import { useAutoPay } from "@root/utils/hooks/useAutoPay";
|
||||||
|
import { useReauthorization } from "@root/utils/hooks/useReauthorization";
|
||||||
|
|
||||||
type PaymentMethod = {
|
type PaymentMethod = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -61,6 +63,8 @@ export default function Payment() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const handleCustomBackNavigation = useHistoryTracker();
|
const handleCustomBackNavigation = useHistoryTracker();
|
||||||
|
|
||||||
|
// Используем обновленный хук useReauthorization для обработки авторизации
|
||||||
|
const { isProcessing } = useReauthorization();
|
||||||
|
|
||||||
//Логика сбора данных из урла "чё мы ваще пришли на эту страницу"
|
//Логика сбора данных из урла "чё мы ваще пришли на эту страницу"
|
||||||
useAutoPay();
|
useAutoPay();
|
||||||
@ -172,168 +176,192 @@ export default function Payment() {
|
|||||||
px: isTablet ? (upMd ? "40px" : "18px") : "20px",
|
px: isTablet ? (upMd ? "40px" : "18px") : "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
{/* Показываем состояние загрузки во время авторизации */}
|
||||||
sx={{
|
{isProcessing && (
|
||||||
mt: "20px",
|
<Box
|
||||||
mb: "40px",
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "10px",
|
flexDirection: "column",
|
||||||
}}
|
alignItems: "center",
|
||||||
>
|
justifyContent: "center",
|
||||||
{window.history.length > 1 && (
|
minHeight: "400px",
|
||||||
<IconButton
|
gap: 2,
|
||||||
onClick={handleCustomBackNavigation}
|
}}
|
||||||
sx={{ p: 0, height: "28px", width: "28px", color: "black" }}
|
>
|
||||||
>
|
<CircularProgress size={60} />
|
||||||
<ArrowBackIcon />
|
<Typography variant="h6" color="text.secondary">
|
||||||
</IconButton>
|
Идёт авторизация...
|
||||||
)}
|
</Typography>
|
||||||
<Typography variant="h4">Способ оплаты</Typography>
|
</Box>
|
||||||
</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
|
{!isProcessing && (
|
||||||
variant="pena-outlined-light"
|
<>
|
||||||
disabled={!isFinite(paymentValue)}
|
<Box
|
||||||
onClick={handlePaymentClick}
|
|
||||||
sx={{
|
sx={{
|
||||||
mt: "auto",
|
mt: "20px",
|
||||||
color: "black",
|
mb: "40px",
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
display: "flex",
|
||||||
"&:hover": {
|
gap: "10px",
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Оплатить
|
{window.history.length > 1 && (
|
||||||
</Button>
|
<IconButton
|
||||||
</Box>
|
onClick={handleCustomBackNavigation}
|
||||||
</Box>
|
sx={{ p: 0, height: "28px", width: "28px", color: "black" }}
|
||||||
<WarnModal open={warnModalOpen} setOpen={setWarnModalOpen} />
|
>
|
||||||
<SorryModal open={sorryModalOpen} setOpen={setSorryModalOpen} />
|
<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>
|
</SectionWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,94 @@ import { logout } from '@root/api/auth';
|
|||||||
import { setNotEnoughMoneyAmount } from '@root/stores/allTypesOfPurchases';
|
import { setNotEnoughMoneyAmount } from '@root/stores/allTypesOfPurchases';
|
||||||
import { clearCustomTariffs } from '@root/stores/customTariffs';
|
import { clearCustomTariffs } from '@root/stores/customTariffs';
|
||||||
import { clearTickets } from '@root/stores/tickets';
|
import { clearTickets } from '@root/stores/tickets';
|
||||||
import { clearUserData, useUserStore } from '@root/stores/user';
|
import { clearUserData, setUserId, useUserStore } from '@root/stores/user';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface QuizAuthParams {
|
||||||
|
action?: string;
|
||||||
|
dif?: string;
|
||||||
|
data?: string;
|
||||||
|
userid?: string;
|
||||||
|
wayback?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const useReauthorization = () => {
|
export const useReauthorization = () => {
|
||||||
const userId = useUserStore(store => store.userId)
|
const userId = useUserStore(store => store.userId);
|
||||||
|
const user = useUserStore(store => store.user);
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Этот эффект сработает при каждом изменении query-параметров
|
// Этот эффект сработает при каждом изменении query-параметров
|
||||||
const params = new URLSearchParams(search);
|
const params = new URLSearchParams(search);
|
||||||
|
|
||||||
|
// Обработка старых параметров (userid, sec)
|
||||||
const URLuserId = params.get('userid');
|
const URLuserId = params.get('userid');
|
||||||
const URLtoken = params.get('sec');
|
const URLtoken = params.get('sec');
|
||||||
|
|
||||||
|
// Обработка новых параметров авторизации
|
||||||
|
const quizParams: QuizAuthParams = {
|
||||||
|
action: params.get("action") || undefined,
|
||||||
|
dif: params.get("dif") || undefined,
|
||||||
|
data: params.get("data") || undefined,
|
||||||
|
userid: params.get("userid") || undefined,
|
||||||
|
wayback: params.get("wayback") || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { action, dif, data: token, userid: quizUserId, wayback } = quizParams;
|
||||||
|
|
||||||
|
// Если есть новые параметры авторизации, обрабатываем их
|
||||||
|
if (action && dif && token && quizUserId) {
|
||||||
|
// Если пользователь уже авторизован и это тот же пользователь, перенаправляем на payment
|
||||||
|
if (user?._id === quizUserId) {
|
||||||
|
let returnUrl = `/payment?action=${action}&dif=${dif}&user=${quizUserId}`;
|
||||||
|
if (wayback) returnUrl += `&wayback=${wayback}`;
|
||||||
|
navigate(returnUrl, { replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если уже обрабатываем авторизацию, не запускаем повторно
|
||||||
|
if (isProcessing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsProcessing(true);
|
||||||
|
|
||||||
|
const processQuizAuth = async () => {
|
||||||
|
try {
|
||||||
|
// Если есть текущий токен, очищаем данные
|
||||||
|
if (getAuthToken()) {
|
||||||
|
clearAuthToken();
|
||||||
|
clearUserData();
|
||||||
|
clearCustomTariffs();
|
||||||
|
clearTickets();
|
||||||
|
setNotEnoughMoneyAmount(0);
|
||||||
|
await logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем новый токен и ID пользователя
|
||||||
|
setAuthToken(token);
|
||||||
|
setUserId(quizUserId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ошибка авторизации:", error);
|
||||||
|
|
||||||
|
// Перенаправляем на внешний сайт в случае ошибки
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "https://quiz.pena.digital/tariffs";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
} finally {
|
||||||
|
setIsProcessing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
processQuizAuth();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработка старых параметров (userid, sec)
|
||||||
if (URLuserId !== userId && URLtoken) {
|
if (URLuserId !== userId && URLtoken) {
|
||||||
// Если есть токен в URL, устанавливаем его
|
// Если есть токен в URL, устанавливаем его
|
||||||
// Очищаем данные только если токен действительно изменился
|
// Очищаем данные только если токен действительно изменился
|
||||||
@ -31,5 +104,9 @@ export const useReauthorization = () => {
|
|||||||
}
|
}
|
||||||
setAuthToken(URLtoken);
|
setAuthToken(URLtoken);
|
||||||
}
|
}
|
||||||
}, [search]);
|
}, [search, userId, user?._id, navigate, isProcessing]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isProcessing,
|
||||||
|
};
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user