Merge branch 'AW-114-payment-preloading' into 'develop'
AW-114-payment-preloading See merge request witapp/aura-webapp!186
This commit is contained in:
commit
d8b1121b92
@ -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} />
|
||||
|
||||
@ -12,6 +12,11 @@
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
|
||||
@ -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] =
|
||||
@ -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;
|
||||
@ -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}
|
||||
|
||||
@ -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"]}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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" && (
|
||||
|
||||
@ -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"]}
|
||||
/>
|
||||
|
||||
@ -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";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user