Merge branch 'develop' into 'main'

develop

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ import Button from "../../components/Button";
import { usePaywall } from "@/hooks/paywall/usePaywall"; import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall"; import { EPlacementKeys } from "@/api/resources/Paywall";
import { addCurrency, ELocalesPlacement } from "@/locales"; import { addCurrency, ELocalesPlacement } from "@/locales";
import { useEffect, useState } from "react"; import { useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import routes from "@/routes"; import routes from "@/routes";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
@ -15,10 +15,6 @@ import { actions } from "@/store";
import { useTranslations } from "@/hooks/translations"; import { useTranslations } from "@/hooks/translations";
import { useDynamicSize } from "@/hooks/useDynamicSize"; import { useDynamicSize } from "@/hooks/useDynamicSize";
import Header from "../../components/Header"; 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"] const placementKey = EPlacementKeys["aura.placement.palmistry.secret.discount"]
@ -39,7 +35,7 @@ function SecretDiscount() {
// const price = (activeProduct?.price || 0) / 100; // const price = (activeProduct?.price || 0) / 100;
const trialPrice = (activeProduct?.trialPrice || 0) / 100; const trialPrice = (activeProduct?.trialPrice || 0) / 100;
// const trialDuration = activeProduct?.trialDuration || 7; // const trialDuration = activeProduct?.trialDuration || 7;
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false); // const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
useEffect(() => { useEffect(() => {
if (!activeProduct) return; if (!activeProduct) return;
@ -60,33 +56,34 @@ function SecretDiscount() {
// paymentFormType: "lightbox" // paymentFormType: "lightbox"
// }); // });
const onPaymentSuccess = () => { // const onPaymentSuccess = () => {
metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]); // metricService.reachGoal(EGoals.PAYMENT_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], { // metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
currency: "USD", // currency: "USD",
value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2), // value: ((activeProduct?.trialPrice || 100) / 100).toFixed(2),
}); // });
return navigate(routes.client.paymentSuccess()) // return navigate(routes.client.paymentSuccess())
} // }
// const onModalClosed = () => { // const onModalClosed = () => {
// // setIsPaymentModalOpen(false); // // setIsPaymentModalOpen(false);
// } // }
const onPaymentError = () => { // const onPaymentError = () => {
metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]); // metricService.reachGoal(EGoals.PAYMENT_ERROR, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
return navigate(routes.client.paymentFail()) // return navigate(routes.client.paymentFail())
} // }
const onModalClosed = () => { // const onModalClosed = () => {
setIsPaymentModalOpen(false); // setIsPaymentModalOpen(false);
} // }
const openPaymentModal = () => { const openPaymentModal = () => {
metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]); // metricService.reachGoal(EGoals.PAYMENT_METHODS_OPENED, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]); // metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED, [EMetrics.KLAVIYO]);
setIsPaymentModalOpen(true); // setIsPaymentModalOpen(true);
// showCreditCardForm(); // showCreditCardForm();
navigate(routes.client.palmistryV1SecretDiscountPaymentModal())
}; };
// useEffect(() => { // useEffect(() => {
@ -105,13 +102,13 @@ function SecretDiscount() {
<section className={styles.container} ref={elementRef} style={{ <section className={styles.container} ref={elementRef} style={{
paddingBottom: `${height + 42}px` paddingBottom: `${height + 42}px`
}}> }}>
<PaymentModal {/* <PaymentModal
// containerClassName={styles["modal-content"]} // containerClassName={styles["modal-content"]}
open={isPaymentModalOpen} open={isPaymentModalOpen}
onClose={onModalClosed} onClose={onModalClosed}
> >
<PaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={placementKey} /> <PaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={placementKey} />
</PaymentModal> </PaymentModal> */}
<Header <Header
className={styles.header} className={styles.header}
classNameTitle={styles["header-title"]} 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 routes, { compatibilityV2Prefix } from "@/routes";
import { Route, Routes } from "react-router-dom"; import { Route, Routes, useNavigate } from "react-router-dom";
import Layout from "./Layout"; import Layout from "./Layout";
import FindHappiness from "@/components/CompatibilityV2/pages/FindHappiness"; import FindHappiness from "@/components/CompatibilityV2/pages/FindHappiness";
import StepperLayout from "./StepperLayout"; import StepperLayout from "./StepperLayout";
@ -42,18 +42,97 @@ import AdditionalPurchasesPalmistry from "@/components/palmistry/AdditionalPurch
import SkipTrial from "@/components/palmistry/AdditionalPurchases/pages/SkipTrial"; import SkipTrial from "@/components/palmistry/AdditionalPurchases/pages/SkipTrial";
import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddConsultant"; import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddConsultant";
import AddGuides from "@/components/palmistry/AdditionalPurchases/pages/AddGuides"; 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, ""); const removePrefix = (path: string) => path.replace(compatibilityV2Prefix, "");
function CompatibilityV2Routes() { function CompatibilityV2Routes() {
const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch(actions.compatibilityV2.update({ fromRedesign: true })); dispatch(actions.compatibilityV2.update({ fromRedesign: true }));
}, [dispatch]); }, [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 ( return (
<Routes> <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={<PrivateOutlet />}>
<Route element={<AdditionalPurchasesPalmistry />}> <Route element={<AdditionalPurchasesPalmistry />}>
<Route <Route

View File

@ -1,5 +1,5 @@
import routes, { palmistryV1Prefix } from "@/routes"; 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 LayoutPalmistryV1 from "./LayoutPalmistryV1";
import FindHappiness from "@/components/PalmistryV1/pages/FindHappiness"; import FindHappiness from "@/components/PalmistryV1/pages/FindHappiness";
import StepperLayoutPalmistryV1 from "./StepperLayoutPalmistryV1"; import StepperLayoutPalmistryV1 from "./StepperLayoutPalmistryV1";
@ -36,18 +36,97 @@ import AdditionalPurchasesPalmistry from "@/components/palmistry/AdditionalPurch
import SkipTrial from "@/components/palmistry/AdditionalPurchases/pages/SkipTrial"; import SkipTrial from "@/components/palmistry/AdditionalPurchases/pages/SkipTrial";
import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddConsultant"; import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddConsultant";
import AddGuides from "@/components/palmistry/AdditionalPurchases/pages/AddGuides"; 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, ""); const removePrefix = (path: string) => path.replace(palmistryV1Prefix, "");
function PalmistryV1Routes() { function PalmistryV1Routes() {
const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch(actions.palmistry.update({ fromRedesign: true })); dispatch(actions.palmistry.update({ fromRedesign: true }));
}, [dispatch]); }, [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 ( return (
<Routes> <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={<PrivateOutlet />}>
<Route element={<AdditionalPurchasesPalmistry />}> <Route element={<AdditionalPurchasesPalmistry />}>
<Route <Route

View File

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