Merge branch 'AW-114-payment-preloading' into 'develop'

AW-114-payment-preloading

See merge request witapp/aura-webapp!186
This commit is contained in:
Daniil Chemerkin 2024-06-17 23:01:46 +00:00
commit d8b1121b92
12 changed files with 41 additions and 269 deletions

View File

@ -7,6 +7,7 @@ interface ModalProps {
isCloseButtonVisible?: boolean;
className?: string;
containerClassName?: string;
type?: "hidden" | "normal";
onClose?: () => void;
}
@ -16,6 +17,7 @@ function Modal({
isCloseButtonVisible = true,
className = "",
containerClassName = "",
type = "normal",
onClose,
}: ModalProps): JSX.Element {
const handleClose = (event: React.MouseEvent) => {
@ -34,9 +36,14 @@ function Modal({
};
}, [open]);
if (!open) return <></>;
if (!open && type === "normal") return <></>;
return (
<div className={`${styles.modal} ${className}`} onClick={handleClose}>
<div
className={`${styles.modal} ${className} ${
type === "hidden" && !open ? styles.hidden : ""
}`}
onClick={handleClose}
>
<div className={`${styles["modal-content"]} ${containerClassName}`}>
{isCloseButtonVisible && (
<button className={styles["modal-close-btn"]} onClick={handleClose} />

View File

@ -12,6 +12,11 @@
z-index: 2000;
}
.hidden {
height: 0;
visibility: hidden;
}
.modal-content {
position: absolute;
background: #fff;

View File

@ -1,6 +1,6 @@
import Title from "@/components/Title";
import styles from "./styles.module.css";
import PaymentMethodsChoice from "../PaymentMethodsChoice";
import PaymentMethodsChoice from "../pages/TrialPayment/components/PaymentMethodsChoice";
import { useEffect, useMemo, useState } from "react";
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
import { Elements } from "@stripe/react-stripe-js";
@ -9,7 +9,7 @@ import { AvailablePaymentMethods, Stripe, loadStripe } from "@stripe/stripe-js";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
import Loader from "@/components/Loader";
import SecurityPayments from "../SecurityPayments";
import SecurityPayments from "../pages/TrialPayment/components/SecurityPayments";
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { useMakePayment } from "@/hooks/payment/useMakePayment";
@ -21,7 +21,7 @@ interface IPaymentModalProps {
activeProduct?: IPaywallProduct;
noTrial?: boolean;
returnUrl?: string;
placementKey?: EPlacementKeys;
placementKey: EPlacementKeys;
}
const getPrice = (product: IPaywallProduct | null) => {
@ -35,7 +35,7 @@ function PaymentModal({
activeProduct,
noTrial,
returnUrl,
placementKey = EPlacementKeys["aura.placement.main"],
placementKey,
}: IPaymentModalProps) {
const navigate = useNavigate();
const [stripePromise, setStripePromise] =

View File

@ -1,197 +0,0 @@
import Title from "@/components/Title";
import styles from "./styles.module.css";
import PaymentMethodsChoice from "../PaymentMethodsChoice";
import { useEffect, useMemo, useState } from "react";
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
import { Elements } from "@stripe/react-stripe-js";
import CheckoutForm from "@/components/PaymentPage/methods/CheckoutForm";
import { AvailablePaymentMethods, Stripe, loadStripe } from "@stripe/stripe-js";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
import { useNavigate } from "react-router-dom";
import routes from "@/routes";
import Loader from "@/components/Loader";
import SecurityPayments from "../SecurityPayments";
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { useMakePayment } from "@/hooks/payment/useMakePayment";
import ExpressCheckoutStripe from "@/components/PaymentPage/methods/ExpressCheckoutStripe";
interface IPaymentModalProps {
activeProduct?: IPaywallProduct;
noTrial?: boolean;
returnUrl?: string;
placementKey?: EPlacementKeys;
}
const getPrice = (product: IPaywallProduct) => {
return (product.trialPrice || 0) / 100;
};
function PaymentModal({
activeProduct,
noTrial,
returnUrl,
placementKey = EPlacementKeys["aura.placement.redesign.main"],
}: IPaymentModalProps) {
const navigate = useNavigate();
const [stripePromise, setStripePromise] =
useState<Promise<Stripe | null> | null>(null);
const activeProductFromStore = useSelector(selectors.selectActiveProduct);
const _activeProduct = activeProduct ? activeProduct : activeProductFromStore;
const { products, paywallId, placementId } = usePaywall({ placementKey });
const {
paymentIntentId,
clientSecret,
returnUrl: checkoutUrl,
paymentType,
publicKey,
isLoading: isLoadingPayment,
error,
} = useMakePayment({
productId: _activeProduct?._id || "",
paywallId,
placementId,
returnPaidUrl: returnUrl,
});
const [availableMethods, setAvailableMethods] = useState<
AvailablePaymentMethods | undefined
>();
const [isLoadingExpressCheckout, setIsLoadingExpressCheckout] =
useState(true);
const isLoading = useMemo(() => {
return isLoadingPayment || isLoadingExpressCheckout;
}, [isLoadingPayment, isLoadingExpressCheckout]);
if (checkoutUrl?.length) {
window.location.href = checkoutUrl;
}
const paymentMethodsButtons = useMemo(() => {
return paymentMethods(availableMethods || null);
}, [availableMethods]);
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(
paymentMethodsButtons[0].id
);
const onSelectPaymentMethod = (method: EPaymentMethod) => {
setSelectedPaymentMethod(method);
};
useEffect(() => {
(async () => {
if (!products?.length || !publicKey) return;
setStripePromise(loadStripe(publicKey));
const isActiveProduct = products.find(
(product) => product._id === _activeProduct?._id
);
if (!_activeProduct || !isActiveProduct) {
navigate(routes.client.trialChoiceV1());
}
})();
}, [_activeProduct, navigate, products, publicKey]);
const onAvailableExpressCheckout = (
isAvailable: boolean,
availableMethods: AvailablePaymentMethods | undefined
) => {
if (isAvailable && availableMethods) {
setAvailableMethods(availableMethods);
return setSelectedPaymentMethod(EPaymentMethod.PAYMENT_BUTTONS);
}
return setAvailableMethods(undefined);
};
if (error?.length) {
return (
<div className={styles["payment-modal"]}>
<Title variant="h3" className={styles.title}>
Something went wrong
</Title>
</div>
);
}
return (
<>
{isLoading && (
<div className={styles["payment-modal"]}>
<div className={styles["payment-loader"]}>
<Loader />
</div>
</div>
)}
<div
className={`${styles["payment-modal"]} ${isLoading ? styles.hide : ""}`}
>
<Title variant="h3" className={styles.title}>
Choose payment method
</Title>
<PaymentMethodsChoice
paymentMethods={paymentMethodsButtons}
selectedPaymentMethod={selectedPaymentMethod}
onSelectPaymentMethod={onSelectPaymentMethod}
/>
{_activeProduct && (
<div>
{!noTrial && (
<>
<p className={styles["sub-plan-description"]}>
You will be charged only{" "}
<b>${getPrice(_activeProduct)} for your 3-day trial.</b>
</p>
<p className={styles["sub-plan-description"]}>
We`ll <b>email you a reminder</b> before your trial period
ends.
</p>
</>
)}
<p className={styles["sub-plan-description"]}>
Cancel anytime. The charge will appear on your bill as witapps.
</p>
</div>
)}
<div className={styles["payment-method-container"]}>
{stripePromise && clientSecret && (
<>
<Elements stripe={stripePromise} options={{ clientSecret }}>
<ExpressCheckoutStripe
clientSecret={clientSecret}
returnUrl={returnUrl}
isHide={
selectedPaymentMethod !== EPaymentMethod.PAYMENT_BUTTONS
}
onAvailable={(_isAvailable, _availableMethods) =>
onAvailableExpressCheckout(_isAvailable, _availableMethods)
}
onChangeLoading={(isLoading) =>
setIsLoadingExpressCheckout(isLoading)
}
/>
</Elements>
<Elements stripe={stripePromise} options={{ clientSecret }}>
<CheckoutForm
confirmType={paymentType}
subscriptionReceiptId={paymentIntentId}
returnUrl={returnUrl}
isHide={selectedPaymentMethod !== EPaymentMethod.CREDIT_CARD}
/>
</Elements>
</>
)}
</div>
<SecurityPayments />
<p className={styles.address}>1123 Rimer Dr Moraga, California 94556</p>
</div>
</>
);
}
export default PaymentModal;

View File

@ -15,7 +15,6 @@ import OftenAsk from "./components/OftenAsk";
import { useEffect, useState } from "react";
import WithPartnerInformation from "./components/WithPartnerInformation";
import Modal from "@/components/Modal";
import PaymentModal from "./components/PaymentModal";
import { trialPaymentPointsList } from "@/data/pointsLists";
import { trialPaymentReviews } from "@/data/reviews";
import TrialPaymentHeader from "./components/Header";
@ -24,6 +23,7 @@ import BackgroundTopBlob from "../../ui/BackgroundTopBlob";
import { useDynamicSize } from "@/hooks/useDynamicSize";
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import PaymentModal from "@/components/PaymentModal";
function TrialPaymentPage() {
const dispatch = useDispatch();
@ -118,8 +118,11 @@ function TrialPaymentPage() {
containerClassName={styles.modal}
open={isOpenPaymentModal}
onClose={handleDiscount}
type="hidden"
>
<PaymentModal />
<PaymentModal
placementKey={EPlacementKeys["aura.placement.redesign.main"]}
/>
</Modal>
<BackgroundTopBlob
width={pageWidth}

View File

@ -3,12 +3,12 @@ import styles from "./styles.module.css";
import MainButton from "@/components/MainButton";
import PaymentDiscountTable from "./PaymentDiscountTable";
import Modal from "@/components/Modal";
import PaymentModal from "../TrialPayment/components/PaymentModal";
import { useEffect, useState } from "react";
import { actions, selectors } from "@/store";
import { useDispatch, useSelector } from "react-redux";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
import PaymentModal from "@/components/PaymentModal";
function TrialPaymentWithDiscount() {
const dispatch = useDispatch();
@ -38,7 +38,7 @@ function TrialPaymentWithDiscount() {
return (
<section className={`${styles.page} page`}>
<Modal open={isOpenPaymentModal} onClose={handleClose}>
<Modal open={isOpenPaymentModal} onClose={handleClose} type="hidden">
<PaymentModal
placementKey={EPlacementKeys["aura.placement.secret.discount"]}
/>

View File

@ -3,7 +3,7 @@ import styles from "./styles.module.css";
import ReservedTimer from "./components/ReservedTimer";
import MainButton from "@/components/MainButton";
import Modal from "@/components/Modal";
import PaymentModal from "../../TrialPayment/components/PaymentModal";
import PaymentModal from "@/components/PaymentModal";
import { useEffect, useState } from "react";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
@ -37,6 +37,7 @@ function MarketingTrialPayment() {
containerClassName={styles.modal}
open={isOpenPaymentModal}
onClose={handleCloseModal}
type="hidden"
>
<PaymentModal
placementKey={EPlacementKeys["aura.placement.email.marketing"]}
@ -52,7 +53,9 @@ function MarketingTrialPayment() {
<p className={styles.description}>No pressure. Cancel anytime</p>
<div className={styles["total-today"]}>
<p className={styles.description}>Total today:</p>
<p className={styles.value}>${(products[0]?.trialPrice / 100).toFixed(2) || 0}</p>
<p className={styles.value}>
${(products[0]?.trialPrice / 100).toFixed(2) || 0}
</p>
</div>
<div className={styles.line} />
<div className={styles["code-container"]}>
@ -71,7 +74,11 @@ function MarketingTrialPayment() {
<p className={styles["sale-description"]}>Save $10 every period</p>
<div className={styles.line} />
<p className={styles["text-description"]}>
You will be charged only <b>${(products[0]?.trialPrice / 100).toFixed(2) || 0} for your 7-day trial.</b>{" "}
You will be charged only{" "}
<b>
${(products[0]?.trialPrice / 100).toFixed(2) || 0} for your 7-day
trial.
</b>{" "}
Subscription <b>renews automatically</b> until cancelled. You{" "}
<b>can cancel at any time</b> before the end of the trial.
</p>

View File

@ -1,54 +0,0 @@
.payment-modal {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 250px;
gap: 25px;
color: #2f2e37;
}
.payment-modal.hide {
min-height: 0;
height: 0;
opacity: 0;
}
.title {
font-weight: 700;
font-size: 20px;
line-height: 20px;
text-align: center;
margin: 0;
}
.sub-plan-description {
font-size: 12px;
text-align: center;
line-height: 150%;
}
.payment-method-container {
width: 100%;
display: flex;
flex-direction: column;
gap: 24px;
}
.address {
margin-bottom: 24px;
text-transform: uppercase;
}
.payment-method {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
}
.address {
color: gray;
font-size: 10px;
}

View File

@ -16,7 +16,7 @@ import OftenAsk from "./components/OftenAsk";
import { useEffect, useState } from "react";
import WithPartnerInformation from "./components/WithPartnerInformation";
import Modal from "@/components/Modal";
import PaymentModal from "./components/PaymentModal";
import PaymentModal from "@/components/PaymentModal";
import { trialPaymentPointsList } from "@/data/pointsLists";
import { trialPaymentReviews } from "@/data/reviews";
import { usePaywall } from "@/hooks/paywall/usePaywall";
@ -111,8 +111,9 @@ function TrialPaymentPage() {
containerClassName={styles.modal}
open={isOpenPaymentModal}
onClose={handleDiscount}
type="hidden"
>
<PaymentModal />
<PaymentModal placementKey={EPlacementKeys["aura.placement.main"]} />
</Modal>
<Header buttonClick={openStripeModal} />
{singleOrWithPartner === "partner" && (

View File

@ -3,7 +3,7 @@ import styles from "./styles.module.css";
import MainButton from "@/components/MainButton";
import PaymentDiscountTable from "./PaymentDiscountTable";
import Modal from "@/components/Modal";
import PaymentModal from "../TrialPayment/components/PaymentModal";
import PaymentModal from "@/components/PaymentModal";
import { useEffect, useState } from "react";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
@ -39,7 +39,7 @@ function TrialPaymentWithDiscount() {
return (
<section className={`${styles.page} page`}>
<Modal open={isOpenPaymentModal} onClose={handleClose}>
<Modal open={isOpenPaymentModal} onClose={handleClose} type="hidden">
<PaymentModal
placementKey={EPlacementKeys["aura.placement.secret.discount"]}
/>

View File

@ -7,7 +7,7 @@ import "./payment-screen.css";
import useSteps, { Step } from "@/hooks/palmistry/use-steps";
import useTimer from "@/hooks/palmistry/use-timer";
import HeaderLogo from "@/components/palmistry/header-logo/header-logo";
import PaymentModal from "@/components/pages/TrialPayment/components/PaymentModal";
import PaymentModal from "@/components/PaymentModal";
import { selectors } from "@/store";
import { EPlacementKeys } from "@/api/resources/Paywall";
import { useSearchParams } from "react-router-dom";