This commit is contained in:
Daniil Chemerkin 2025-02-13 15:15:04 +00:00
parent 74cd9921fd
commit 63329f56d6
11 changed files with 616 additions and 174 deletions

View File

@ -11,14 +11,10 @@ import Stars from "../../components/Stars";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
import { useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import routes from "@/routes";
import PaymentModal from "@/components/Payment/PaymentModal";
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
import Toast from "@/components/pages/ABDesign/v1/components/Toast";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
const placementKey = EPlacementKeys['aura.placement.compatibility.v2'];
// const placementKey = EPlacementKeys['aura.placement.compatibility.v2'];
function Payment() {
const navigate = useNavigate();
@ -34,63 +30,64 @@ function Payment() {
// const isShowPaymentModal = useSelector(
// selectors.selectCompatibilityV2IsShowPaymentModalV1
// );
const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
const [isPaymentError, setIsPaymentError] = useState(false);
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
// const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
// const [isPaymentError, setIsPaymentError] = useState(false);
// const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
const onPaymentError = (error?: string) => {
setIsPaymentError(true);
if (error === "Product not found") {
return navigate(routes.client.compatibilityV2TrialChoice());
}
metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
}
const onPaymentSuccess = () => {
setIsPaymentSuccess(true);
// metricService.reachGoal(EGoals.PAYMENT_SUCCESS);
// if (activeProductFromStore) {
// metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// currency: "USD",
// value: ((activeProductFromStore.trialPrice || 100) / 100).toFixed(2),
// });
// const onPaymentError = (error?: string) => {
// setIsPaymentError(true);
// if (error === "Product not found") {
// return navigate(routes.client.compatibilityV2TrialChoice());
// }
// metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// }
metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
currency: "USD",
value: ((activeProductFromStore?.trialPrice || 100) / 100).toFixed(2),
});
setTimeout(() => {
navigate(routes.client.compatibilityV2SkipTrial());
}, 1500);
}
// const onPaymentSuccess = () => {
// setIsPaymentSuccess(true);
// // metricService.reachGoal(EGoals.PAYMENT_SUCCESS);
// // if (activeProductFromStore) {
// // metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// // currency: "USD",
// // value: ((activeProductFromStore.trialPrice || 100) / 100).toFixed(2),
// // });
// // }
const onModalClosed = () => {
setIsPaymentModalOpen(false);
navigate(routes.client.compatibilityV2SaveOff());
}
// metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// currency: "USD",
// value: ((activeProductFromStore?.trialPrice || 100) / 100).toFixed(2),
// });
// setTimeout(() => {
// navigate(routes.client.compatibilityV2SkipTrial());
// }, 1500);
// }
const showModal = () => {
// const onModalClosed = () => {
// setIsPaymentModalOpen(false);
// navigate(routes.client.compatibilityV2SaveOff());
// }
const handlePayment = () => {
// dispatch(actions.compatibilityV2.setIsShowPaymentModalV1(true));
metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
setIsPaymentModalOpen(true);
// metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
// setIsPaymentModalOpen(true);
navigate(routes.client.compatibilityV2PaymentModal());
};
useEffect(() => {
window.onpopstate = function (_event) {
if (document.location.toString() === `${window.location.origin}${routes.client.compatibilityV2TrialPayment()}`) {
return navigate(routes.client.compatibilityV2SaveOff());
}
};
return () => {
setTimeout(() => {
window.onpopstate = null;
}, 0);
};
// useEffect(() => {
// window.onpopstate = function (_event) {
// if (document.location.toString() === `${window.location.origin}${routes.client.compatibilityV2TrialPayment()}`) {
// return navigate(routes.client.compatibilityV2SaveOff());
// }
// };
// return () => {
// setTimeout(() => {
// window.onpopstate = null;
// }, 0);
// };
}, [])
// }, [])
useEffect(() => {
if (!products.length) return;
@ -102,13 +99,13 @@ function Payment() {
return (
<>
<PaymentModal
{/* <PaymentModal
// containerClassName={styles["modal-content"]}
open={isPaymentModalOpen}
onClose={onModalClosed}
>
<PaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={placementKey} />
</PaymentModal>
</PaymentModal> */}
<div className={styles["app-number-one"]}>
<p className={styles.text}>
{translate("/payment.app_number_one", {
@ -153,11 +150,11 @@ function Payment() {
</div>
<Guarantees />
{/* {!isShowPaymentModal && ( */}
<Button className={styles.button} onClick={showModal}>
<Button className={styles.button} onClick={handlePayment}>
{translate("/payment.get_personal_prediction")}
</Button>
{/* )} */}
{isPaymentError && (
{/* {isPaymentError && (
<Toast
variant="error"
classNameContainer={styles.toast}
@ -174,7 +171,7 @@ function Payment() {
<p>Payment successful</p>
</Toast>
)}
)} */}
</>
);

View File

@ -5,10 +5,11 @@
}
.button {
position: fixed;
position: sticky;
bottom: calc(0dvh + 26px);
max-width: 343px;
font-size: 24px;
margin-top: 16px;
}
.payment-modal-hide {
@ -23,6 +24,7 @@
0% {
transform: translateY(150%);
}
100% {
transform: translateY(0);
}

View File

@ -7,7 +7,7 @@ import Button from "../../components/Button";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
import { addCurrency, ELocalesPlacement } from "@/locales";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import routes from "@/routes";
import { useDispatch } from "react-redux";
@ -16,9 +16,6 @@ import { useTranslations } from "@/hooks/translations";
import { useDynamicSize } from "@/hooks/useDynamicSize";
import Header from "../../components/Header";
import PaymentModal from "@/components/Payment/PaymentModal";
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
const placementKey = EPlacementKeys["aura.placement.compatibility.v2.secret.discount"]
@ -48,7 +45,7 @@ function SecretDiscount() {
}))
}, [activeProduct])
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
// const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
// const {
// error,
@ -60,29 +57,30 @@ function SecretDiscount() {
// paymentFormType: "lightbox"
// });
const onPaymentSuccess = () => {
metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
currency: "USD",
value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2),
});
return navigate(routes.client.paymentSuccess())
}
// const onPaymentSuccess = () => {
// metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// currency: "USD",
// value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2),
// });
// return navigate(routes.client.paymentSuccess())
// }
const onModalClosed = () => {
setIsPaymentModalOpen(false);
}
// const onModalClosed = () => {
// setIsPaymentModalOpen(false);
// }
const onPaymentError = () => {
metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
return navigate(routes.client.paymentFail())
}
// const onPaymentError = () => {
// metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// return navigate(routes.client.paymentFail())
// }
const openPaymentModal = () => {
metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
setIsPaymentModalOpen(true);
// metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
// setIsPaymentModalOpen(true);
// showCreditCardForm();
navigate(routes.client.compatibilityV2SecretDiscountPaymentModal());
};
// useEffect(() => {
@ -101,13 +99,13 @@ function SecretDiscount() {
<section className={styles.container} ref={elementRef} style={{
paddingBottom: `${height + 42}px`
}}>
<PaymentModal
{/* <PaymentModal
// containerClassName={styles["modal-content"]}
open={isPaymentModalOpen}
onClose={onModalClosed}
>
<PaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={placementKey} />
</PaymentModal>
</PaymentModal> */}
<Header
className={styles.header}
classNameTitle={styles["header-title"]}

View File

@ -11,14 +11,10 @@ import Stars from "../../components/Stars";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
import { useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import routes from "@/routes";
import PaymentModal from "@/components/Payment/PaymentModal";
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
import Toast from "@/components/pages/ABDesign/v1/components/Toast";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
const placementKey = EPlacementKeys['aura.placement.palmistry.redesign'];
// const placementKey = EPlacementKeys['aura.placement.palmistry.redesign'];
function Payment() {
const navigate = useNavigate();
@ -34,63 +30,65 @@ function Payment() {
// const isShowPaymentModal = useSelector(
// selectors.selectPalmistryIsShowPaymentModalV1
// );
const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
const [isPaymentError, setIsPaymentError] = useState(false);
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
// const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
// const [isPaymentError, setIsPaymentError] = useState(false);
// const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
const onPaymentError = (error?: string) => {
setIsPaymentError(true);
if (error === "Product not found") {
return navigate(routes.client.palmistryV1TrialChoice());
}
metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
}
const onPaymentSuccess = () => {
setIsPaymentSuccess(true);
// metricService.reachGoal(EGoals.PAYMENT_SUCCESS);
// if (activeProductFromStore) {
// metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// currency: "USD",
// value: ((activeProductFromStore.trialPrice || 100) / 100).toFixed(2),
// });
// const onPaymentError = (error?: string) => {
// setIsPaymentError(true);
// if (error === "Product not found") {
// return navigate(routes.client.palmistryV1TrialChoice());
// }
// metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// }
metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
currency: "USD",
value: ((activeProductFromStore?.trialPrice || 100) / 100).toFixed(2),
});
setTimeout(() => {
navigate(routes.client.palmistryV1SkipTrial());
}, 1500);
}
// const onPaymentSuccess = () => {
// setIsPaymentSuccess(true);
// // metricService.reachGoal(EGoals.PAYMENT_SUCCESS);
// // if (activeProductFromStore) {
// // metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// // currency: "USD",
// // value: ((activeProductFromStore.trialPrice || 100) / 100).toFixed(2),
// // });
// // }
const onModalClosed = () => {
setIsPaymentModalOpen(false);
navigate(routes.client.palmistryV1SaveOff());
}
// metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// currency: "USD",
// value: ((activeProductFromStore?.trialPrice || 100) / 100).toFixed(2),
// });
// setTimeout(() => {
// navigate(routes.client.palmistryV1SkipTrial());
// }, 1500);
// }
const showModal = () => {
// const onModalClosed = () => {
// setIsPaymentModalOpen(false);
// navigate(routes.client.palmistryV1SaveOff());
// }
const handlePayment = () => {
// dispatch(actions.compatibilityV2.setIsShowPaymentModalV1(true));
metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
setIsPaymentModalOpen(true);
// metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
// setIsPaymentModalOpen(true);
navigate(routes.client.palmistryV1PaymentModal());
};
useEffect(() => {
window.onpopstate = function (_event) {
if (document.location.toString() === `${window.location.origin}${routes.client.palmistryV1TrialPayment()}`) {
return navigate(routes.client.palmistryV1SaveOff());
}
};
return () => {
setTimeout(() => {
window.onpopstate = null;
}, 0);
};
// useEffect(() => {
// window.onpopstate = function (_event) {
// console.log("####PAYMENT: ", document.location.toString());
}, [])
// if (document.location.toString() === `${window.location.origin}${routes.client.palmistryV1TrialPayment()}`) {
// return navigate(routes.client.palmistryV1SaveOff());
// }
// };
// return () => {
// setTimeout(() => {
// window.onpopstate = null;
// }, 0);
// };
// }, [])
useEffect(() => {
if (!products.length) return;
@ -102,13 +100,13 @@ function Payment() {
return (
<>
<PaymentModal
{/* <PaymentModal
// containerClassName={styles["modal-content"]}
open={isPaymentModalOpen}
onClose={onModalClosed}
>
<PaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={placementKey} />
</PaymentModal>
</PaymentModal> */}
<div className={styles["app-number-one"]}>
<p className={styles.text}>
{translate("/payment.app_number_one", {
@ -153,11 +151,11 @@ function Payment() {
</div>
<Guarantees />
{/* {!isShowPaymentModal && ( */}
<Button className={styles.button} onClick={showModal}>
<Button className={styles.button} onClick={handlePayment}>
{translate("/payment.get_personal_prediction")}
</Button>
{/* )} */}
{isPaymentError && (
{/* {isPaymentError && (
<Toast
variant="error"
classNameContainer={styles.toast}
@ -174,7 +172,7 @@ function Payment() {
<p>Payment successful</p>
</Toast>
)}
)} */}
</>
);
}

View File

@ -5,10 +5,11 @@
}
.button {
position: fixed;
position: sticky;
bottom: calc(0dvh + 26px);
max-width: 343px;
font-size: 24px;
margin-top: 16px;
}
.payment-modal-hide {
@ -23,6 +24,7 @@
0% {
transform: translateY(150%);
}
100% {
transform: translateY(0);
}

View File

@ -7,7 +7,7 @@ import Button from "../../components/Button";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
import { addCurrency, ELocalesPlacement } from "@/locales";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import routes from "@/routes";
import { useDispatch } from "react-redux";
@ -15,10 +15,6 @@ import { actions } from "@/store";
import { useTranslations } from "@/hooks/translations";
import { useDynamicSize } from "@/hooks/useDynamicSize";
import Header from "../../components/Header";
import PaymentModal from "@/components/Payment/PaymentModal";
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
// import { getFormattedPrice } from "@/utils/price.utils";
const placementKey = EPlacementKeys["aura.placement.palmistry.secret.discount"]
@ -39,7 +35,7 @@ function SecretDiscount() {
// const price = (activeProduct?.price || 0) / 100;
const trialPrice = (activeProduct?.trialPrice || 0) / 100;
// const trialDuration = activeProduct?.trialDuration || 7;
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
// const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
useEffect(() => {
if (!activeProduct) return;
@ -60,33 +56,34 @@ function SecretDiscount() {
// paymentFormType: "lightbox"
// });
const onPaymentSuccess = () => {
metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
currency: "USD",
value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2),
});
return navigate(routes.client.paymentSuccess())
}
// const onPaymentSuccess = () => {
// metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
// currency: "USD",
// value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2),
// });
// return navigate(routes.client.paymentSuccess())
// }
// const onModalClosed = () => {
// // setIsPaymentModalOpen(false);
// }
const onPaymentError = () => {
metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
return navigate(routes.client.paymentFail())
}
// const onPaymentError = () => {
// metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// return navigate(routes.client.paymentFail())
// }
const onModalClosed = () => {
setIsPaymentModalOpen(false);
}
// const onModalClosed = () => {
// setIsPaymentModalOpen(false);
// }
const openPaymentModal = () => {
metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
setIsPaymentModalOpen(true);
// metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
// metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
// setIsPaymentModalOpen(true);
// showCreditCardForm();
navigate(routes.client.palmistryV1SecretDiscountPaymentModal())
};
// useEffect(() => {
@ -105,13 +102,13 @@ function SecretDiscount() {
<section className={styles.container} ref={elementRef} style={{
paddingBottom: `${height + 42}px`
}}>
<PaymentModal
{/* <PaymentModal
// containerClassName={styles["modal-content"]}
open={isPaymentModalOpen}
onClose={onModalClosed}
>
<PaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={placementKey} />
</PaymentModal>
</PaymentModal> */}
<Header
className={styles.header}
classNameTitle={styles["header-title"]}

View File

@ -0,0 +1,189 @@
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
import styles from "./styles.module.scss";
import { useTranslations } from "@/hooks/translations";
import { addCurrency, ELocalesPlacement } from "@/locales";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
import { EPaymentMethod } from "@/data/paymentMethods";
import Loader from "@/components/Loader";
import Title from "@/components/Title";
import PaymentMethodsChoice from "../../PaymentMethodsChoice";
import CheckoutForm from "../CheckoutForm";
import { useEffect, useState } from "react";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
import Toast from "@/components/pages/ABDesign/v1/components/Toast";
import Header from "@/components/PalmistryV1/components/Header";
import { useNavigate } from "react-router-dom";
interface IPaymentPageProps {
isSinglePayment?: boolean;
placementKey: EPlacementKeys;
className?: string;
onError?: (error?: string) => void;
onSuccess?: () => void;
onBack?: () => void;
onPopState?: () => void;
}
const getPrice = (product: IPaywallProduct | null) => {
if (!product) {
return 0;
}
return (product.trialPrice || 0) / 100;
}
function PaymentPage({
isSinglePayment = false,
placementKey,
className = "",
onError,
onSuccess,
onBack,
onPopState
}: IPaymentPageProps) {
const navigate = useNavigate();
const { translate } = useTranslations(ELocalesPlacement.V1);
const currency = useSelector(selectors.selectCurrency);
const activeProduct = useSelector(selectors.selectActiveProduct);
const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
const [isPaymentError, setIsPaymentError] = useState(false);
const isLoading = false;
const paymentMethodsButtons = [
{
id: EPaymentMethod.CREDIT_CARD,
icon: "/payment-form/credit-card.svg",
title: translate("payment_modal.credit_card")
}
]
function onPaymentError(error?: string | undefined): void {
setIsPaymentError(true);
if (error !== "Product not found") {
metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
}
onError?.(error);
}
function onPaymentSuccess(): void {
setIsPaymentSuccess(true);
metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
currency: "USD",
value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2),
});
onSuccess?.();
}
function onModalClosed(): void {
// onBack?.();
}
const handleBack = () => {
onBack ? onBack() : navigate(-1);
}
useEffect(() => {
metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
}, [])
useEffect(() => {
const handlePopState = (_event: PopStateEvent) => {
onPopState?.();
};
window.addEventListener('popstate', handlePopState);
return () => {
setTimeout(() => {
window.removeEventListener('popstate', handlePopState);
}, 0);
};
}, []);
return (
<div className={styles.container}>
<Header
className={styles.header}
classNameTitle={styles["header-title"]}
isBackButtonVisible={true}
onBack={handleBack}
/>
{isLoading && (
<div className={styles["payment-modal"]}>
<div className={styles["payment-loader"]}>
<Loader />
</div>
</div>
)}
<div
className={`${styles["payment-modal"]} ${isLoading ? styles.hide : ""} ${className}`}
>
{!isSinglePayment && <Title variant="h3" className={styles.title}>
{translate("payment_modal.title")}
</Title>}
<PaymentMethodsChoice
paymentMethods={paymentMethodsButtons}
selectedPaymentMethod={paymentMethodsButtons[0].id}
onSelectPaymentMethod={() => { }}
/>
{!isSinglePayment && activeProduct && (
<div>
<p className={styles["sub-plan-description"]}>
{translate("payment_modal.description", {
priceForDays: (
<b>
{translate("payment_modal.price_for_days", {
trialPrice: addCurrency(
getPrice(activeProduct),
currency
),
trialDuration: activeProduct?.trialDuration,
})}
</b>
),
emailReminder: (
<b>{translate("payment_modal.email_reminder")}</b>
),
})}
</p>
</div>
)}
<div className={styles["payment-method-container"]}>
{!!activeProduct && <CheckoutForm
placementKey={placementKey}
activeProduct={activeProduct}
onError={onPaymentError}
onSuccess={onPaymentSuccess}
onModalClosed={onModalClosed}
/>}
</div>
<p className={styles.address}>{translate("payment_modal.address")}</p>
</div>
{isPaymentError && (
<Toast
variant="error"
classNameContainer={styles.toast}
>
<p>Something went wrong</p>
</Toast>
)}
{isPaymentSuccess && (
<Toast
variant="success"
classNameContainer={styles.toast}
>
<p>Payment successful</p>
</Toast>
)}
</div>
)
}
export default PaymentPage

View File

@ -0,0 +1,97 @@
.container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 26px;
height: fit-content;
min-height: 100dvh;
max-width: 560px;
margin: 0 auto;
background: #F1F5FF;
}
.payment-modal {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 250px;
gap: 0;
color: #000;
& * {
font-family: "SF Pro Text", sans-serif;
}
}
.payment-modal.hide {
min-height: 0;
height: 0;
opacity: 0;
}
.title {
font-weight: 500;
font-size: 20px;
line-height: 20px;
text-align: center;
margin: 0;
margin-bottom: 18px;
}
.sub-plan-description {
font-size: 13px;
text-align: center;
line-height: 150%;
white-space: pre-wrap;
font-weight: 300;
margin: 18px 0;
&>b {
font-weight: 500;
}
}
.payment-method-container {
width: 100%;
display: flex;
flex-direction: column;
gap: 24px;
}
.address {
text-transform: uppercase;
font-size: 13px;
font-weight: 300;
text-align: center;
margin-top: 16px;
}
.payment-method {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
}
.address {
color: gray;
font-size: 10px;
}
.header {
padding: 8px 0 30px;
}
.header-title {
color: #275CA7;
}
.toast {
width: 100%;
margin-top: 16px;
position: sticky;
bottom: calc(0dvh + 16px);
z-index: 1000;
}

View File

@ -1,5 +1,5 @@
import routes, { compatibilityV2Prefix } from "@/routes";
import { Route, Routes } from "react-router-dom";
import { Route, Routes, useNavigate } from "react-router-dom";
import Layout from "./Layout";
import FindHappiness from "@/components/CompatibilityV2/pages/FindHappiness";
import StepperLayout from "./StepperLayout";
@ -42,18 +42,97 @@ import AdditionalPurchasesPalmistry from "@/components/palmistry/AdditionalPurch
import SkipTrial from "@/components/palmistry/AdditionalPurchases/pages/SkipTrial";
import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddConsultant";
import AddGuides from "@/components/palmistry/AdditionalPurchases/pages/AddGuides";
import PaymentPage from "@/components/Payment/nmi/PaymentPage";
import { EPlacementKeys } from "@/api/resources/Paywall";
const removePrefix = (path: string) => path.replace(compatibilityV2Prefix, "");
function CompatibilityV2Routes() {
const navigate = useNavigate();
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.compatibilityV2.update({ fromRedesign: true }));
}, [dispatch]);
function onPaymentError(error?: string | undefined): void {
if (error === "Product not found") {
return navigate(routes.client.compatibilityV2TrialChoice());
}
}
function onPaymentSuccess(): void {
setTimeout(() => {
navigate(routes.client.compatibilityV2SkipTrial());
}, 1500);
}
function onBack(): void {
navigate(routes.client.compatibilityV2SaveOff());
}
function onPopState(): void {
if (document.location.toString() === `${window.location.origin}${routes.client.compatibilityV2Payment()}`) {
navigate(routes.client.compatibilityV2SaveOff());
}
}
function onPaymentErrorDiscount(error?: string | undefined): void {
if (error === "Product not found") {
return navigate(routes.client.compatibilityV2SecretDiscount());
}
}
function onPaymentSuccessDiscount(): void {
setTimeout(() => {
navigate(routes.client.compatibilityV2SkipTrial());
}, 1500);
}
return (
<Routes>
<Route element={<PrivateOutlet />}>
<Route element={<AdditionalPurchasesPalmistry />}>
<Route
path={removePrefix(routes.client.compatibilityV2SkipTrial())}
element={<SkipTrial />}
/>
<Route
path={removePrefix(routes.client.compatibilityV2AddConsultant())}
element={<AddConsultant />}
/>
<Route
path={removePrefix(routes.client.compatibilityV2AddGuides())}
element={<AddGuides />}
/>
</Route>
</Route>
<Route
element={
<CheckSubscriptionOutlet
subscribedReturnUrl={routes.client.compatibilityV2SkipTrial()}
/>
}
>
<Route
path={removePrefix(routes.client.compatibilityV2PaymentModal())}
element={<PaymentPage
placementKey={EPlacementKeys["aura.placement.compatibility.v2"]}
onError={onPaymentError}
onSuccess={onPaymentSuccess}
onBack={onBack}
onPopState={onPopState}
/>}
/>
<Route
path={removePrefix(routes.client.compatibilityV2SecretDiscountPaymentModal())}
element={<PaymentPage
placementKey={EPlacementKeys["aura.placement.compatibility.v2.secret.discount"]}
onError={onPaymentErrorDiscount}
onSuccess={onPaymentSuccessDiscount}
/>}
/>
</Route>
<Route element={<PrivateOutlet />}>
<Route element={<AdditionalPurchasesPalmistry />}>
<Route

View File

@ -1,5 +1,5 @@
import routes, { palmistryV1Prefix } from "@/routes";
import { Route, Routes } from "react-router-dom";
import { Route, Routes, useNavigate } from "react-router-dom";
import LayoutPalmistryV1 from "./LayoutPalmistryV1";
import FindHappiness from "@/components/PalmistryV1/pages/FindHappiness";
import StepperLayoutPalmistryV1 from "./StepperLayoutPalmistryV1";
@ -36,18 +36,97 @@ import AdditionalPurchasesPalmistry from "@/components/palmistry/AdditionalPurch
import SkipTrial from "@/components/palmistry/AdditionalPurchases/pages/SkipTrial";
import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddConsultant";
import AddGuides from "@/components/palmistry/AdditionalPurchases/pages/AddGuides";
import PaymentPage from "@/components/Payment/nmi/PaymentPage";
import { EPlacementKeys } from "@/api/resources/Paywall";
const removePrefix = (path: string) => path.replace(palmistryV1Prefix, "");
function PalmistryV1Routes() {
const navigate = useNavigate();
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.palmistry.update({ fromRedesign: true }));
}, [dispatch]);
function onPaymentError(error?: string | undefined): void {
if (error === "Product not found") {
return navigate(routes.client.palmistryV1TrialChoice());
}
}
function onPaymentSuccess(): void {
setTimeout(() => {
navigate(routes.client.palmistryV1SkipTrial());
}, 1500);
}
function onBack(): void {
navigate(routes.client.palmistryV1SaveOff());
}
function onPopState(): void {
if (document.location.toString() === `${window.location.origin}${routes.client.palmistryV1Payment()}`) {
navigate(routes.client.palmistryV1SaveOff());
}
}
function onPaymentErrorDiscount(error?: string | undefined): void {
if (error === "Product not found") {
return navigate(routes.client.palmistryV1SecretDiscount());
}
}
function onPaymentSuccessDiscount(): void {
setTimeout(() => {
navigate(routes.client.palmistryV1SkipTrial());
}, 1500);
}
return (
<Routes>
<Route element={<PrivateOutlet />}>
<Route element={<AdditionalPurchasesPalmistry />}>
<Route
path={removePrefix(routes.client.palmistryV1SkipTrial())}
element={<SkipTrial />}
/>
<Route
path={removePrefix(routes.client.palmistryV1AddConsultant())}
element={<AddConsultant />}
/>
<Route
path={removePrefix(routes.client.palmistryV1AddGuides())}
element={<AddGuides />}
/>
</Route>
</Route>
<Route
element={
<CheckSubscriptionOutlet
subscribedReturnUrl={routes.client.palmistryV1SkipTrial()}
/>
}
>
<Route
path={removePrefix(routes.client.palmistryV1PaymentModal())}
element={<PaymentPage
placementKey={EPlacementKeys["aura.placement.palmistry.redesign"]}
onError={onPaymentError}
onSuccess={onPaymentSuccess}
onBack={onBack}
onPopState={onPopState}
/>}
/>
<Route
path={removePrefix(routes.client.palmistryV1SecretDiscountPaymentModal())}
element={<PaymentPage
placementKey={EPlacementKeys["aura.placement.palmistry.secret.discount"]}
onError={onPaymentErrorDiscount}
onSuccess={onPaymentSuccessDiscount}
/>}
/>
</Route>
<Route element={<PrivateOutlet />}>
<Route element={<AdditionalPurchasesPalmistry />}>
<Route

View File

@ -199,6 +199,8 @@ const routes = {
palmistryV1TrialChoiceVideo: () => [palmistryV1Prefix, "trial-choice-video"].join("/"),
palmistryV1TrialPayment: () => [palmistryV1Prefix, "trial-payment"].join("/"),
palmistryV1Payment: () => [palmistryV1Prefix, "payment"].join("/"),
palmistryV1PaymentModal: () => [palmistryV1Prefix, "payment-modal"].join("/"),
palmistryV1SecretDiscountPaymentModal: () => [palmistryV1Prefix, "secret-discount-payment-modal"].join("/"),
palmistryV1SaveOff: () => [palmistryV1Prefix, "save-off"].join("/"),
palmistryV1SecretDiscount: () => [palmistryV1Prefix, "secret-discount"].join("/"),
palmistryOnboardingV1: () => [palmistryV1Prefix, "onboarding"].join("/"),
@ -229,6 +231,8 @@ const routes = {
compatibilityV2TrialChoiceVideo: () => [compatibilityV2Prefix, "trial-choice-video"].join("/"),
compatibilityV2TrialPayment: () => [compatibilityV2Prefix, "trial-payment"].join("/"),
compatibilityV2Payment: () => [compatibilityV2Prefix, "payment"].join("/"),
compatibilityV2PaymentModal: () => [compatibilityV2Prefix, "payment-modal"].join("/"),
compatibilityV2SecretDiscountPaymentModal: () => [compatibilityV2Prefix, "secret-discount-payment-modal"].join("/"),
compatibilityV2SaveOff: () => [compatibilityV2Prefix, "save-off"].join("/"),
compatibilityV2SecretDiscount: () => [compatibilityV2Prefix, "secret-discount"].join("/"),
compatibilityV2Onboarding: () => [compatibilityV2Prefix, "onboarding"].join("/"),