pipe + автопокупка после оплаты тарифа
This commit is contained in:
parent
cf3b60e19f
commit
e29e41d8b8
@ -4,7 +4,7 @@ import { clearUserData, setCustomerAccount, setUser, setUserAccount, useUserStor
|
||||
import ContactFormModal from "@ui_kit/ContactForm";
|
||||
import FloatingSupportChat from "@ui_kit/FloatingSupportChat";
|
||||
import PrivateRoute from "@ui_kit/PrivateRoute";
|
||||
import { useAfterpay } from "@utils/hooks/useAfterpay";
|
||||
import { useAfterPay } from "@utils/hooks/useAutoPay";
|
||||
import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import type { SuspenseProps } from "react";
|
||||
@ -113,7 +113,7 @@ export default function App() {
|
||||
},
|
||||
});
|
||||
|
||||
useAfterpay();
|
||||
useAfterPay();
|
||||
|
||||
if (location.state?.redirectTo)
|
||||
return (
|
||||
|
||||
@ -21,7 +21,7 @@ export default function CreateButtons({ mt }: string) {
|
||||
}}
|
||||
>
|
||||
{
|
||||
user.user._id === "6692e068983ee77f8e1e682e" &&
|
||||
user?.user?._id === "6692e068983ee77f8e1e682e" &&
|
||||
<Link to="/gallery" style={{ textDecoration: "none" }}>
|
||||
<Button
|
||||
sx={{
|
||||
|
||||
38
src/stores/notEnoughMoneyAmount.ts
Normal file
38
src/stores/notEnoughMoneyAmount.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import moment from "moment";
|
||||
import { create } from "zustand";
|
||||
import { devtools } from "zustand/middleware";
|
||||
|
||||
interface CartStore {
|
||||
notEnoughMoneyAmount: number;
|
||||
siteReadyPayCart: Record<string, string> | null;
|
||||
}
|
||||
const initialState: CartStore = {
|
||||
notEnoughMoneyAmount: 0,
|
||||
siteReadyPayCart: null
|
||||
}
|
||||
|
||||
//Была попытка оплатить корзину. Тут записанна недостающая сумма.
|
||||
export const useNotEnoughMoneyAmount = create<CartStore>()(
|
||||
devtools(
|
||||
(get, set) => initialState,
|
||||
{
|
||||
name: "notEnoughMoneyAmount",
|
||||
enabled: process.env.NODE_ENV === "development",
|
||||
trace: true,
|
||||
actionsBlacklist: "rejected",
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const setNotEnoughMoneyAmount = (amount: number) => useNotEnoughMoneyAmount.setState({ notEnoughMoneyAmount: amount });
|
||||
|
||||
export const setSiteReadyPayCart = (flag: Record<string, string> | null) => useNotEnoughMoneyAmount.setState({ siteReadyPayCart: flag });
|
||||
export const startPayCartProcess = (userId: string) => setSiteReadyPayCart({ [userId]: moment().add(20, 'minutes').format("X") });
|
||||
export const cancelPayCartProcess = () => setSiteReadyPayCart(null);
|
||||
export const calcTimeOfReadyPayCart = (deadline: string) => {
|
||||
const ready = Number(deadline) > Number(moment().format("X"))
|
||||
if (!ready) {
|
||||
cancelPayCartProcess()
|
||||
}
|
||||
return ready
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { setCash } from "@root/cash";
|
||||
import { useUserStore } from "@root/user";
|
||||
|
||||
import { cartApi } from "@api/cart";
|
||||
import { currencyFormatter } from "../../pages/Tariffs/tariffsUtils/currencyFormatter";
|
||||
|
||||
const MINUTE = 1000 * 60;
|
||||
|
||||
export const useAfterpay = () => {
|
||||
const userId = useUserStore((state) => state.userId);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const checkPayment = () => {
|
||||
const redirectUrl = new URL(window.location.href);
|
||||
const paymentUserId = redirectUrl.searchParams.get("userid");
|
||||
redirectUrl.searchParams.set("afterpay", "false");
|
||||
navigate(redirectUrl);
|
||||
|
||||
if (userId !== paymentUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payCartPendingRequestDeadline = localStorage.getItem(
|
||||
"payCartPendingRequestDeadline",
|
||||
);
|
||||
const deadline = payCartPendingRequestDeadline
|
||||
? Number(payCartPendingRequestDeadline)
|
||||
: Date.now() + 20 * MINUTE;
|
||||
|
||||
localStorage.setItem("payCartPendingRequestDeadline", deadline.toString());
|
||||
|
||||
let tryCount = 0;
|
||||
tryPayCart();
|
||||
|
||||
async function tryPayCart() {
|
||||
tryCount += 1;
|
||||
|
||||
const [data, payCartError] = await cartApi.pay();
|
||||
if (data !== null)
|
||||
setCash(
|
||||
currencyFormatter.format(Number(data.wallet.cash) / 100),
|
||||
Number(data.wallet.cash),
|
||||
Number(data.wallet.cash) / 100,
|
||||
);
|
||||
|
||||
if (!payCartError || Date.now() > deadline) {
|
||||
localStorage.removeItem("payCartPendingRequestDeadline");
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(tryPayCart, tryCount > 5 ? MINUTE : MINUTE / 6);
|
||||
}
|
||||
};
|
||||
|
||||
const checkParams = () => {
|
||||
const afterpay = new URLSearchParams(window.location.search).get(
|
||||
"afterpay",
|
||||
);
|
||||
|
||||
const payCartPendingRequestDeadline = localStorage.getItem(
|
||||
"payCartPendingRequestDeadline",
|
||||
);
|
||||
const deadline = payCartPendingRequestDeadline
|
||||
? Number(payCartPendingRequestDeadline)
|
||||
: Date.now() + 20 * MINUTE;
|
||||
if (Date.now() <= deadline) {
|
||||
if (afterpay) {
|
||||
checkPayment();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setInterval(checkParams, 5000);
|
||||
checkParams();
|
||||
};
|
||||
@ -1,41 +1,62 @@
|
||||
import { payCart } from "@root/api/cart";
|
||||
import { setSiteReadyPayCart } from "@root/stores/notEnoughMoneyAmount";
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
import { cartApi } from "@api/cart";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import moment from "moment";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { calcTimeOfReadyPayCart, cancelPayCartProcess, startPayCartProcess, useNotEnoughMoneyAmount } from "@/stores/notEnoughMoneyAmount";
|
||||
|
||||
export const useAfterPay = () => {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const paymentUserId = searchParams.get("userid");
|
||||
const userId = useUserStore(store => store.userId)
|
||||
const userAccount = useUserStore(state => state.userAccount);
|
||||
const siteReadyPayCart = useNotEnoughMoneyAmount(state => state.siteReadyPayCart);
|
||||
|
||||
const purpose = searchParams.get("purpose");
|
||||
const from = searchParams.get("from") || "hub";
|
||||
const action = searchParams.get("action");
|
||||
const paymentUserId = searchParams.get("userid");
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
//Звёзды сошлись, будем оплачивать корзину
|
||||
if (from !== "quiz" && paymentUserId && paymentUserId === userId) {
|
||||
//Чистим url адрес от параметров. (Если нет action. Если есть - значит мы пришли из квиза)
|
||||
if (action === null) navigate(`/tariffs`, {
|
||||
replace: true,
|
||||
});
|
||||
if (paymentUserId && paymentUserId === userId) {
|
||||
|
||||
if (purpose === "paycart") {
|
||||
setSearchParams({}, { replace: true });
|
||||
(async () => {
|
||||
|
||||
//Проверяем можем ли мы оплатить корзину здесь и сейчас
|
||||
const [, payCartError] = await payCart();
|
||||
const [, payCartError] = await cartApi.pay();
|
||||
console.log("попытка оплаты не удалась")
|
||||
|
||||
if (payCartError) {
|
||||
//Не получилось купить корзину. Ставим флаг, что сайт в состоянии ожидания пополнения счёта для оплаты (потом проверим .isAfter)
|
||||
setSiteReadyPayCart({ [paymentUserId]: moment().add(20, 'minutes').format("x") })
|
||||
//Не получилось купить корзину. Ставим флаг, что сайт в состоянии ожидания пополнения счёта для оплаты
|
||||
startPayCartProcess(paymentUserId)
|
||||
} else {
|
||||
enqueueSnackbar("Товары успешно приобретены")
|
||||
cancelPayCartProcess()
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
}, [purpose, from, paymentUserId])
|
||||
}, [purpose, paymentUserId])
|
||||
|
||||
useEffect(() => {
|
||||
if (userId !== null && siteReadyPayCart !== null && siteReadyPayCart[userId] !== undefined) {
|
||||
const deadline = siteReadyPayCart[userId]
|
||||
if (calcTimeOfReadyPayCart(deadline)) {
|
||||
|
||||
//Время ещё не вышло. У нас стоит флаг покупать корзину если время не вышло.
|
||||
(async () => {
|
||||
console.log("Время ещё не вышло. У нас стоит флаг покупать корзину если время не вышло.")
|
||||
const [, payCartError] = await cartApi.pay();
|
||||
|
||||
if (!payCartError) {
|
||||
enqueueSnackbar("Товары успешно приобретены")
|
||||
cancelPayCartProcess()
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
}, [userAccount, userId, siteReadyPayCart])
|
||||
}
|
||||
67
src/utils/hooks/usePipeSubscriber.ts
Normal file
67
src/utils/hooks/usePipeSubscriber.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { Ticket, UserAccount, UserName, getAuthToken, useSSESubscription } from "@frontend/kitui"
|
||||
import { useUserStore } from "@root/user";
|
||||
import { useSSETab } from "./useSSETab";
|
||||
import { cancelPayCartProcess } from "@/stores/notEnoughMoneyAmount";
|
||||
import { setCash } from "@/stores/cash";
|
||||
import { currencyFormatter } from "@/pages/Tariffs/tariffsUtils/currencyFormatter";
|
||||
import { inCart } from "@/pages/Tariffs/Tariffs";
|
||||
|
||||
type Ping = [{ event: "ping" }]
|
||||
|
||||
type SomeChange = [{
|
||||
"id": UserAccount["_id"],
|
||||
"userId": UserAccount["userId"],
|
||||
"cart": UserAccount["cart"],
|
||||
"wallet": {
|
||||
"cash": UserAccount["wallet"]["cash"],
|
||||
"currency": UserAccount["wallet"]["currency"],
|
||||
"spent": UserAccount["wallet"]["spent"],
|
||||
"purchasesAmount": UserAccount["wallet"]["purchasesAmount"],
|
||||
"money": UserAccount["wallet"]["money"],
|
||||
"lastPaymentId": string;
|
||||
},
|
||||
"name": UserName,
|
||||
"status": UserAccount["status"],
|
||||
"isDeleted": UserAccount["isDeleted"],
|
||||
"createdAt": UserAccount["createdAt"];
|
||||
"updatedAt": UserAccount["updatedAt"];
|
||||
"from": string;
|
||||
"partner": string;
|
||||
}]
|
||||
|
||||
type PipeMessage = Ping | SomeChange
|
||||
|
||||
export const usePipeSubscriber = () => {
|
||||
const token = getAuthToken();
|
||||
const userId = useUserStore((state) => state.userId);
|
||||
const { isActiveSSETab, updateSSEValue } = useSSETab<Ticket[]>("pipe");
|
||||
|
||||
useSSESubscription({
|
||||
enabled: Boolean(token) && Boolean(userId) && isActiveSSETab,
|
||||
url:
|
||||
process.env.REACT_APP_DOMAIN +
|
||||
`/customer/v1.0.1/account/pipe?Authorization=${token}`,
|
||||
onNewData: (data) => {
|
||||
let message = data[0] as PipeMessage
|
||||
updateSSEValue(message)
|
||||
|
||||
//Пропускаем пингование
|
||||
if ('event' in message && message.event === "ping") return
|
||||
|
||||
|
||||
if ('cart' in message) {
|
||||
//корзина была изменена, мы больше не хотим покупать
|
||||
cancelPayCartProcess()
|
||||
inCart()
|
||||
}
|
||||
else if ('wallet' in message) {
|
||||
setCash(
|
||||
currencyFormatter.format(Number(message.wallet) / 100),
|
||||
Number(message.wallet),
|
||||
Number(message.wallet) / 100,
|
||||
);
|
||||
}
|
||||
},
|
||||
marker: "pipe",
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user