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 ContactFormModal from "@ui_kit/ContactForm";
|
||||||
import FloatingSupportChat from "@ui_kit/FloatingSupportChat";
|
import FloatingSupportChat from "@ui_kit/FloatingSupportChat";
|
||||||
import PrivateRoute from "@ui_kit/PrivateRoute";
|
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 { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import type { SuspenseProps } from "react";
|
import type { SuspenseProps } from "react";
|
||||||
@ -113,7 +113,7 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useAfterpay();
|
useAfterPay();
|
||||||
|
|
||||||
if (location.state?.redirectTo)
|
if (location.state?.redirectTo)
|
||||||
return (
|
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" }}>
|
<Link to="/gallery" style={{ textDecoration: "none" }}>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
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 { cartApi } from "@api/cart";
|
||||||
import { setSiteReadyPayCart } from "@root/stores/notEnoughMoneyAmount";
|
import { useUserStore } from "@/stores/user";
|
||||||
import { useUserStore } from "@root/stores/user";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
import { calcTimeOfReadyPayCart, cancelPayCartProcess, startPayCartProcess, useNotEnoughMoneyAmount } from "@/stores/notEnoughMoneyAmount";
|
||||||
|
|
||||||
export const useAfterPay = () => {
|
export const useAfterPay = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const paymentUserId = searchParams.get("userid");
|
|
||||||
const userId = useUserStore(store => store.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 purpose = searchParams.get("purpose");
|
||||||
const from = searchParams.get("from") || "hub";
|
const paymentUserId = searchParams.get("userid");
|
||||||
const action = searchParams.get("action");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//Звёзды сошлись, будем оплачивать корзину
|
//Звёзды сошлись, будем оплачивать корзину
|
||||||
if (from !== "quiz" && paymentUserId && paymentUserId === userId) {
|
if (paymentUserId && paymentUserId === userId) {
|
||||||
//Чистим url адрес от параметров. (Если нет action. Если есть - значит мы пришли из квиза)
|
|
||||||
if (action === null) navigate(`/tariffs`, {
|
|
||||||
replace: true,
|
|
||||||
});
|
|
||||||
if (purpose === "paycart") {
|
if (purpose === "paycart") {
|
||||||
|
setSearchParams({}, { replace: true });
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
||||||
//Проверяем можем ли мы оплатить корзину здесь и сейчас
|
//Проверяем можем ли мы оплатить корзину здесь и сейчас
|
||||||
const [, payCartError] = await payCart();
|
const [, payCartError] = await cartApi.pay();
|
||||||
|
console.log("попытка оплаты не удалась")
|
||||||
|
|
||||||
if (payCartError) {
|
if (payCartError) {
|
||||||
//Не получилось купить корзину. Ставим флаг, что сайт в состоянии ожидания пополнения счёта для оплаты (потом проверим .isAfter)
|
//Не получилось купить корзину. Ставим флаг, что сайт в состоянии ожидания пополнения счёта для оплаты
|
||||||
setSiteReadyPayCart({ [paymentUserId]: moment().add(20, 'minutes').format("x") })
|
startPayCartProcess(paymentUserId)
|
||||||
} else {
|
} else {
|
||||||
enqueueSnackbar("Товары успешно приобретены")
|
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