+
) : null}
- {stripePromise && clientSecret && (
+ {!isLoading && (
+ <>
+
+ Choose payment method
+
+
{email}
+ >
+ )}
+ {stripePromise && clientSecret && subscriptionReceiptId && (
-
+
+ {activeSubPlan && (
+
+ )}
+
)}
diff --git a/src/components/PaymentPage/methods/Stripe/styles.module.css b/src/components/PaymentPage/methods/Stripe/styles.module.css
new file mode 100644
index 0000000..1a43dab
--- /dev/null
+++ b/src/components/PaymentPage/methods/Stripe/styles.module.css
@@ -0,0 +1,38 @@
+.page {
+ /* position: relative; */
+ position: static;
+ /* height: calc(100vh - 50px);
+ max-height: -webkit-fill-available; */
+ display: flex;
+ justify-items: center;
+ justify-content: center;
+ gap: 16px;
+}
+
+.payment-loader {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.cross {
+ position: absolute;
+ top: -36px;
+ right: 28px;
+ width: 22px;
+ height: 22px;
+ cursor: pointer;
+ z-index: 9;
+}
+
+.title {
+ font-size: 27px;
+ font-weight: 700;
+ margin: 0;
+}
+
+.email {
+ font-size: 17px;
+ font-weight: 500;
+ margin: 0;
+}
diff --git a/src/components/StripePage/ApplePayButton/index.tsx b/src/components/StripePage/ApplePayButton/index.tsx
index 0039355..ba3e47d 100644
--- a/src/components/StripePage/ApplePayButton/index.tsx
+++ b/src/components/StripePage/ApplePayButton/index.tsx
@@ -70,10 +70,10 @@ function ApplePayButton({
if (stripeError) {
// Show error to your customer (e.g., insufficient funds)
- return;
+ return e.complete("fail");
}
-
- navigate(`${routes.client.paymentResult()}/${subscriptionReceiptId}`);
+ navigate(`${routes.client.paymentResult()}/${subscriptionReceiptId}/`);
+ e.complete("success");
// Show a success message to your customer
// There's a risk of the customer closing the window before callback
// execution. Set up a webhook or plugin to listen for the
diff --git a/src/components/pages/AdditionalDiscount/index.tsx b/src/components/pages/AdditionalDiscount/index.tsx
new file mode 100644
index 0000000..e3b5c3b
--- /dev/null
+++ b/src/components/pages/AdditionalDiscount/index.tsx
@@ -0,0 +1,40 @@
+import Title from "@/components/Title";
+import styles from "./styles.module.css";
+import MainButton from "@/components/MainButton";
+import { useNavigate } from "react-router-dom";
+import routes from "@/routes";
+
+function AdditionalDiscount() {
+ const navigate = useNavigate();
+
+ const handleNext = () => {
+ navigate(routes.client.trialPaymentWithDiscount());
+ };
+
+ return (
+
+
+ Save 65% off!
+
+
+
+

+
+ 65% off on your personalized plan
+
+
+
+

+
7-day trial
+
+
+ $9 instead of $19
+
+
+ Get secret discount!
+
+
+ );
+}
+
+export default AdditionalDiscount;
diff --git a/src/components/pages/AdditionalDiscount/styles.module.css b/src/components/pages/AdditionalDiscount/styles.module.css
new file mode 100644
index 0000000..3d8e28a
--- /dev/null
+++ b/src/components/pages/AdditionalDiscount/styles.module.css
@@ -0,0 +1,69 @@
+.page {
+ position: relative;
+ height: fit-content;
+ min-height: 100dvh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ background-position-y: top;
+ background-position-x: center;
+ background-size: contain;
+ background-repeat: no-repeat;
+ color: #fff;
+ padding-bottom: 64px;
+ background-image: url("/ellipse.webp");
+ background-color: #fff0f0;
+ color: #333333;
+}
+
+.title {
+ font-size: 32px;
+ line-height: 44px;
+ margin-bottom: 20px;
+}
+
+.discount-point {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: start;
+ gap: 20px;
+ width: 100%;
+ max-width: 262px;
+ margin-top: 20px;
+}
+
+.discount-point > img {
+ width: 48px;
+}
+
+.discount-point-description {
+ font-weight: 700;
+ font-size: 18px;
+ line-height: 140%;
+ color: #000;
+}
+
+.discount-description {
+ font-size: 16px;
+ margin-top: 20px;
+}
+
+.discount-description > span {
+ color: #8e8cf0;
+ font-weight: 700;
+}
+
+.button {
+ background: rgb(187, 107, 217);
+ height: 50px;
+ min-height: 0;
+ min-width: 0;
+ max-width: 300px;
+ box-shadow: rgba(0, 0, 0, 0.25) 0px 4px 4px 0px;
+ border-radius: 16px;
+ margin-top: 16px;
+ font-size: 16px;
+ font-weight: normal;
+}
diff --git a/src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx
new file mode 100644
index 0000000..950eea6
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx
@@ -0,0 +1,30 @@
+import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
+import styles from "./styles.module.css";
+
+interface IPaymentMethodsChoiceProps {
+ selectedPaymentMethod: EPaymentMethod;
+ onSelectPaymentMethod: (method: EPaymentMethod) => void;
+}
+
+function PaymentMethodsChoice({
+ selectedPaymentMethod,
+ onSelectPaymentMethod,
+}: IPaymentMethodsChoiceProps) {
+ return (
+
+ {paymentMethods.map((method, index) => (
+
onSelectPaymentMethod(method.id)}
+ key={index}
+ >
+ {method.component}
+
+ ))}
+
+ );
+}
+
+export default PaymentMethodsChoice;
diff --git a/src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css
new file mode 100644
index 0000000..fb9e06c
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css
@@ -0,0 +1,23 @@
+.payment-methods {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+}
+
+.payment-method {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ border: 2px solid rgb(232, 232, 252);
+ border-radius: 12px;
+ max-width: 160px;
+ height: 68px;
+ padding-top: 8px;
+ padding-bottom: 8px;
+}
+
+.payment-method.active {
+ border-radius: 12px;
+ border: 2px solid rgb(114, 112, 192);
+}
diff --git a/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx
new file mode 100644
index 0000000..5fb253f
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx
@@ -0,0 +1,23 @@
+import MainButton from "@/components/MainButton";
+import styles from "./styles.module.css";
+import Loader from "@/components/Loader";
+
+interface IPayPalButton {
+ isLoading: boolean;
+ handlePayPalButton: () => void;
+}
+
+function PayPalButton({ isLoading, handlePayPalButton }: IPayPalButton) {
+ return (
+
+ {!isLoading &&
}
+ {isLoading && }
+
+ );
+}
+
+export default PayPalButton;
diff --git a/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css
new file mode 100644
index 0000000..b75606d
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css
@@ -0,0 +1,9 @@
+.pay-pal-button {
+ width: 100%;
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #ffc43a;
+ border-radius: 7px;
+}
diff --git a/src/components/pages/TrialPayment/components/PaymentModal/index.tsx b/src/components/pages/TrialPayment/components/PaymentModal/index.tsx
new file mode 100644
index 0000000..472a63b
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/PaymentModal/index.tsx
@@ -0,0 +1,232 @@
+import Title from "@/components/Title";
+import styles from "./styles.module.css";
+import PaymentMethodsChoice from "../PaymentMethodsChoice";
+import { useEffect, useState } from "react";
+import { EPaymentMethod } from "@/data/paymentMethods";
+import { Elements } from "@stripe/react-stripe-js";
+import ApplePayButton from "@/components/StripePage/ApplePayButton";
+import CheckoutForm from "@/components/PaymentPage/methods/Stripe/CheckoutForm";
+import { Stripe, loadStripe } from "@stripe/stripe-js";
+import { useDispatch, useSelector } from "react-redux";
+import { actions, selectors } from "@/store";
+import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
+import { useApi } from "@/api";
+import { useNavigate } from "react-router-dom";
+import routes from "@/routes";
+import { useTranslation } from "react-i18next";
+import { useAuth } from "@/auth";
+import Loader from "@/components/Loader";
+import { getPriceFromTrial } from "@/services/price";
+import SecurityPayments from "../SecurityPayments";
+import PayPalButton from "./components/PayPalButton";
+
+function PaymentModal() {
+ const { i18n } = useTranslation();
+ const locale = i18n.language;
+ const api = useApi();
+ const { token } = useAuth();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const activeSubPlan = useSelector(selectors.selectActiveSubPlan);
+ const [payPalSubPlan, setPayPalSubPlan] = useState
();
+ const subscriptionReceiptFromStore = useSelector(
+ selectors.selectSubscriptionReceipt
+ );
+ const [stripePromise, setStripePromise] =
+ useState | null>(null);
+ const [clientSecret, setClientSecret] = useState("");
+ const [subscriptionReceiptId, setSubscriptionReceiptId] =
+ useState("");
+ const [subPlans, setSubPlans] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isLoadingPayPal, setIsLoadingPayPal] = useState(false);
+ const [isError, setIsError] = useState(false);
+ const [errors, setErrors] = useState("");
+
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(
+ EPaymentMethod.CREDIT_CARD
+ );
+
+ const onSelectPaymentMethod = (method: EPaymentMethod) => {
+ setSelectedPaymentMethod(method);
+ };
+
+ useEffect(() => {
+ (async () => {
+ const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
+ setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
+ const { sub_plans } = await api.getSubscriptionPlans({ locale });
+ setSubPlans(sub_plans);
+ const isActiveSubPlan = sub_plans.find(
+ (subPlan) => subPlan.id === activeSubPlan?.id
+ );
+ if (!activeSubPlan || !isActiveSubPlan) {
+ navigate(routes.client.priceList());
+ }
+ })();
+ }, [activeSubPlan, api, locale, navigate]);
+
+ useEffect(() => {
+ (async () => {
+ try {
+ const { subscription_receipt } = await api.createSubscriptionReceipt({
+ token,
+ way: "stripe",
+ subscription_receipt: {
+ sub_plan_id: activeSubPlan?.id || "stripe.7",
+ },
+ });
+
+ if (!subscription_receipt && !subscriptionReceiptFromStore) {
+ setIsError(true);
+ setIsLoading(false);
+ } else if (!subscription_receipt && subscriptionReceiptFromStore) {
+ const { id } = subscriptionReceiptFromStore;
+ const { client_secret } = subscriptionReceiptFromStore.data;
+ setSubscriptionReceiptId(id);
+ setClientSecret(client_secret);
+ setIsLoading(false);
+ } else if (subscription_receipt) {
+ dispatch(
+ actions.payment.update({
+ subscriptionReceipt: subscription_receipt,
+ })
+ );
+ const { id } = subscription_receipt;
+ const { client_secret } = subscription_receipt.data;
+ setSubscriptionReceiptId(id);
+ setClientSecret(client_secret);
+ setIsLoading(false);
+ }
+ console.log(subscription_receipt);
+ } catch (error) {
+ if (subscriptionReceiptFromStore) {
+ console.log(1);
+
+ const { id } = subscriptionReceiptFromStore;
+ const { client_secret } = subscriptionReceiptFromStore.data;
+ setSubscriptionReceiptId(id);
+ setClientSecret(client_secret);
+ setIsLoading(false);
+ return;
+ } else {
+ setIsError(true);
+ setIsLoading(false);
+ }
+ console.log(error);
+ }
+ })();
+ }, [activeSubPlan?.id, api, dispatch, subscriptionReceiptFromStore, token]);
+
+ useEffect(() => {
+ if (!subPlans) return;
+ const paypalPlan = subPlans
+ .filter((plan: ISubscriptionPlan) => plan.provider === "paypal")
+ .filter((plan: ISubscriptionPlan) => {
+ if (activeSubPlan?.trial && plan?.trial) return true;
+ if (!activeSubPlan?.trial && !plan?.trial) return true;
+ return false;
+ })
+ .find((plan: ISubscriptionPlan) => {
+ if (activeSubPlan?.trial && plan?.trial) {
+ return plan?.trial?.price_cents === activeSubPlan?.trial?.price_cents;
+ }
+ if (!activeSubPlan?.trial && !plan?.trial) {
+ return plan?.name === activeSubPlan?.name;
+ }
+ return false;
+ });
+ setPayPalSubPlan(paypalPlan);
+ }, [activeSubPlan?.name, activeSubPlan?.trial, subPlans]);
+
+ const handlePayPalButton = async () => {
+ setIsLoadingPayPal(true);
+ const {
+ subscription_receipt: { data },
+ } = await api.createSubscriptionReceipt({
+ token,
+ way: "paypal",
+ subscription_receipt: {
+ sub_plan_id: payPalSubPlan?.id || "paypal.6",
+ },
+ });
+ if (!data?.links) {
+ return setErrors("Something went wrong. Please try again later.");
+ }
+ const link = data.links.find((link) => link.rel === "approve");
+ if (!link) {
+ return setErrors("Something went wrong. Please try again later.");
+ }
+ setIsLoadingPayPal(false);
+ window.location.href = link.href;
+ };
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (isError) {
+ return (
+
+
+ Something went wrong
+
+
+ );
+ }
+
+ return (
+
+
+ Choose payment method
+
+
+ {activeSubPlan && (
+
+ You will be charged only{" "}
+
+ ${getPriceFromTrial(activeSubPlan?.trial)} for your 3-day trial.
+
+
+ )}
+
+ {stripePromise && clientSecret && subscriptionReceiptId && (
+
+ {selectedPaymentMethod === EPaymentMethod.PAYPAL_OR_APPLE_PAY && (
+ <>
+
+ {payPalSubPlan && (
+
+ )}
+ {!!errors.length && {errors}
}
+ >
+ )}
+
+ {selectedPaymentMethod === EPaymentMethod.CREDIT_CARD && (
+
+ )}
+
+ )}
+
+
+
+ );
+}
+
+export default PaymentModal;
diff --git a/src/components/pages/TrialPayment/components/PaymentModal/styles.module.css b/src/components/pages/TrialPayment/components/PaymentModal/styles.module.css
new file mode 100644
index 0000000..900e309
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/PaymentModal/styles.module.css
@@ -0,0 +1,25 @@
+.payment-modal {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 250px;
+ gap: 25px;
+}
+
+.title {
+ font-weight: 800;
+ font-size: 20px;
+ line-height: 20px;
+ text-align: center;
+ margin: 0;
+}
+
+.sub-plan-description {
+ font-size: 12px;
+ text-align: center;
+}
+
+.payment-method-container {
+ width: 100%;
+}
diff --git a/src/components/pages/TrialPayment/components/SecurityPayments/index.tsx b/src/components/pages/TrialPayment/components/SecurityPayments/index.tsx
new file mode 100644
index 0000000..160a317
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/SecurityPayments/index.tsx
@@ -0,0 +1,10 @@
+import styles from "./styles.module.css";
+
+function SecurityPayments() {
+ return
+

+
Guaranteed security payments
+
;
+}
+
+export default SecurityPayments;
diff --git a/src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css b/src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css
new file mode 100644
index 0000000..77bdb21
--- /dev/null
+++ b/src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css
@@ -0,0 +1,12 @@
+.container {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+}
+
+.text {
+ font-size: 12px;
+ font-weight: 400;
+}
diff --git a/src/components/pages/TrialPayment/index.tsx b/src/components/pages/TrialPayment/index.tsx
index 4ff0337..8a841bf 100755
--- a/src/components/pages/TrialPayment/index.tsx
+++ b/src/components/pages/TrialPayment/index.tsx
@@ -4,8 +4,8 @@ import PersonalInformation from "./components/PersonalInformation";
import styles from "./styles.module.css";
import Goal from "./components/Goal";
import PaymentTable from "./components/PaymentTable";
-import { useSelector } from "react-redux";
-import { selectors } from "@/store";
+import { useDispatch, useSelector } from "react-redux";
+import { actions, selectors } from "@/store";
import { Navigate, useNavigate, useParams } from "react-router-dom";
import routes from "@/routes";
import { getZodiacSignByDate } from "@/services/zodiac-sign";
@@ -19,10 +19,13 @@ import { useApi } from "@/api";
import { getClientLocale } from "@/locales";
import { Locale } from "@/components/PaymentTable";
import WithPartnerInformation from "./components/WithPartnerInformation";
+import Modal from "@/components/Modal";
+import PaymentModal from "./components/PaymentModal";
const locale = getClientLocale() as Locale;
function TrialPaymentPage() {
+ const dispatch = useDispatch();
const api = useApi();
const navigate = useNavigate();
const birthdate = useSelector(selectors.selectBirthdate);
@@ -42,6 +45,7 @@ function TrialPaymentPage() {
const [activeSubPlan, setActiveSubPlan] = useState(
activeSubPlanFromStore
);
+ const [isOpenPaymentModal, setIsOpenPaymentModal] = useState(false);
const [marginTopTitle, setMarginTopTitle] = useState(360);
const [singleOrWithPartner, setSingleOrWithPartner] = useState<
"single" | "partner"
@@ -81,9 +85,10 @@ function TrialPaymentPage() {
);
if (targetSubPlan) {
setActiveSubPlan(targetSubPlan);
+ dispatch(actions.payment.update({ activeSubPlan: targetSubPlan }));
}
}
- }, [subPlan, subPlans]);
+ }, [dispatch, subPlan, subPlans]);
useEffect(() => {
if (["relationship", "married"].includes(flowChoice)) {
@@ -103,13 +108,21 @@ function TrialPaymentPage() {
return ;
}
- const handleNext = () => {
- navigate(routes.client.paymentStripe());
+ const handleDiscount = () => {
+ setIsOpenPaymentModal(false);
+ navigate(routes.client.additionalDiscount());
+ };
+
+ const openStripeModal = () => {
+ setIsOpenPaymentModal(true);
};
return (
-
+
+
+
+
{singleOrWithPartner === "partner" && (
-
+
-
+
);
}
diff --git a/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx
new file mode 100644
index 0000000..b62b2f7
--- /dev/null
+++ b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx
@@ -0,0 +1,43 @@
+import Title from "@/components/Title";
+import styles from "./styles.module.css";
+import { useSelector } from "react-redux";
+import { selectors } from "@/store";
+import { getPriceFromTrial } from "@/services/price";
+
+function PaymentDiscountTable() {
+ const activeSub = useSelector(selectors.selectActiveSubPlan);
+
+ return (
+
+
+ You get a secret discount!
+
+
No pressure. Cancel anytime.
+
+
+

+
Secret discount applied!
+
+
+ -30%
+ -65%
+
+
+
+
Your cost per 1 month after trial:
+
+ $19
+ $9
+
+
+
You save $30
+
+
+
Total today:
+ {activeSub &&
${getPriceFromTrial(activeSub.trial)}}
+
+
+ );
+}
+
+export default PaymentDiscountTable;
diff --git a/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css
new file mode 100644
index 0000000..b41ff23
--- /dev/null
+++ b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css
@@ -0,0 +1,156 @@
+.container {
+ padding: 16px 12px;
+ border-radius: 8px;
+ margin-top: 20px;
+ width: 100%;
+ background-color: #fbfbff;
+}
+
+.title {
+ font-size: 18px;
+ line-height: 28px;
+ text-align: center;
+ color: #0f0f0f;
+}
+
+.no-pressure {
+ font-weight: 600;
+ font-size: 14px;
+ line-height: 25px;
+ text-align: center;
+ margin-top: 4px;
+ color: #4f4f4f;
+}
+
+.applied {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: linear-gradient(
+ 95.17deg,
+ rgb(207, 139, 243) -16.49%,
+ rgb(167, 112, 239) -15.14%,
+ rgb(253, 185, 155) 115.23%
+ );
+ border-radius: 6px;
+ margin-top: 16px;
+ padding: 3px 9px;
+}
+
+.present-image {
+ width: 20px;
+}
+
+.applied .description {
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 24px;
+ color: rgb(251, 251, 255);
+}
+
+.applied > .side {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.discount {
+ position: relative;
+ font-weight: 400;
+ font-size: 14px;
+ color: rgb(51, 51, 51);
+ line-height: 19px;
+ margin-right: 10px;
+}
+
+.discount::before {
+ position: absolute;
+ content: "";
+ left: -2px;
+ top: 50%;
+ right: -2px;
+ border-top: 1px solid rgb(235, 87, 87);
+ border-right-color: rgb(235, 87, 87);
+ border-bottom-color: rgb(235, 87, 87);
+ border-left-color: rgb(235, 87, 87);
+ transform: rotate(8deg);
+}
+
+.applied strong {
+ font-weight: 800;
+ font-size: 14px;
+ line-height: 19px;
+ color: rgb(15, 15, 15);
+}
+
+.cost-container {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 8px;
+}
+
+.cost-container > p {
+ font-size: 14px;
+ line-height: 24px;
+ font-weight: 600;
+ color: rgb(79, 79, 79);
+}
+
+.cost-container .discount {
+ color: rgb(130, 130, 130);
+}
+
+.cost-container strong {
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 19px;
+ color: rgb(51, 51, 51);
+}
+
+.save {
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 24px;
+ color: rgb(32, 31, 31);
+}
+
+.line {
+ height: 1px;
+ background-color: rgb(153, 116, 246);
+ margin-top: 16px;
+ margin-bottom: 16px;
+ box-sizing: content-box;
+}
+
+.total-container {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.total-container > p {
+ font-size: 16px;
+ font-weight: 600;
+ line-height: 24px;
+ color: rgb(15, 15, 15);
+}
+
+.total-container strong {
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 24px;
+ background: linear-gradient(
+ 165.54deg,
+ rgb(20, 19, 51) -33.39%,
+ rgb(32, 34, 97) 15.89%,
+ rgb(84, 60, 151) 55.84%,
+ rgb(105, 57, 162) 74.96%
+ )
+ text;
+ -webkit-text-fill-color: transparent;
+}
diff --git a/src/components/pages/TrialPaymentWithDiscount/index.tsx b/src/components/pages/TrialPaymentWithDiscount/index.tsx
new file mode 100644
index 0000000..972cdd9
--- /dev/null
+++ b/src/components/pages/TrialPaymentWithDiscount/index.tsx
@@ -0,0 +1,47 @@
+import Title from "@/components/Title";
+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 { useState } from "react";
+
+function TrialPaymentWithDiscount() {
+ const [isOpenPaymentModal, setIsOpenPaymentModal] = useState(false);
+
+ const handleClose = () => {
+ setIsOpenPaymentModal(false);
+ };
+
+ return (
+
+
+
+
+
+
+ You get a secret discount!
+
+
+ setIsOpenPaymentModal(true)}
+ >
+ Start your 3-day trial
+
+
+ By continuing you agree that if you don't cancel prior to the end of the
+ 3-days trial, you will automatically be charged $9 for the introductory
+ period of 30 days thereafter the standard rate of $9 every 30 days until
+ you cancel in settings. Learn more about cancellation and refund policy
+ in Subscription terms.
+
+
+ );
+}
+
+export default TrialPaymentWithDiscount;
diff --git a/src/components/pages/TrialPaymentWithDiscount/styles.module.css b/src/components/pages/TrialPaymentWithDiscount/styles.module.css
new file mode 100644
index 0000000..4049a7a
--- /dev/null
+++ b/src/components/pages/TrialPaymentWithDiscount/styles.module.css
@@ -0,0 +1,58 @@
+.page {
+ position: relative;
+ height: fit-content;
+ min-height: 100dvh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ color: #fff;
+ padding-bottom: 64px;
+ background-color: #fff0f0;
+}
+
+.party-popper {
+ width: 32px;
+}
+
+.title {
+ margin-top: 11px;
+ font-size: 24px;
+ line-height: 33px;
+ text-align: center;
+ background: linear-gradient(
+ 165.54deg,
+ rgb(20, 19, 51) -33.39%,
+ rgb(32, 34, 97) 15.89%,
+ rgb(84, 60, 151) 55.84%,
+ rgb(105, 57, 162) 74.96%
+ )
+ text;
+ -webkit-text-fill-color: transparent;
+ font-weight: 700;
+}
+
+.button {
+ margin-top: 32px;
+ border-radius: 30px;
+ min-height: 0;
+ height: 50px;
+ font-size: 20px;
+ font-weight: normal;
+ line-height: 22px;
+ width: 100%;
+ min-width: 0;
+ max-width: 360px;
+ background: #27ae60;
+ color: #fbfbff;
+}
+
+.policy {
+ color: rgb(51, 51, 51);
+ font-size: 13px;
+ font-weight: 400;
+ line-height: 20px;
+ margin-top: 28px;
+ padding-bottom: 10px;
+ max-width: 400px;
+}
diff --git a/src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx b/src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx
new file mode 100644
index 0000000..83124c8
--- /dev/null
+++ b/src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx
@@ -0,0 +1,12 @@
+import styles from "./styles.module.css"
+
+function CreditCard() {
+ return (
+
+

+
Credit Card
+
+ )
+}
+
+export default CreditCard
\ No newline at end of file
diff --git a/src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css b/src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css
new file mode 100644
index 0000000..f272c57
--- /dev/null
+++ b/src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css
@@ -0,0 +1,14 @@
+.container {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+}
+
+.text {
+ font-weight: 600;
+ line-height: 21px;
+ color: rgb(99, 99, 157);
+}
diff --git a/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx
new file mode 100644
index 0000000..9b9b4b1
--- /dev/null
+++ b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx
@@ -0,0 +1,10 @@
+import styles from "./styles.module.css";
+
+function PayPalOrApplePay() {
+ return
+

+

+
;
+}
+
+export default PayPalOrApplePay;
diff --git a/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css
new file mode 100644
index 0000000..7d1360f
--- /dev/null
+++ b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css
@@ -0,0 +1,12 @@
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ gap: 8px;
+}
+
+.container > img {
+ height: 16px;
+}
\ No newline at end of file
diff --git a/src/data/paymentMethods.tsx b/src/data/paymentMethods.tsx
new file mode 100644
index 0000000..bafe93a
--- /dev/null
+++ b/src/data/paymentMethods.tsx
@@ -0,0 +1,23 @@
+import CreditCard from "@/components/ui/PaymentMethodsButtons/CreditCard";
+import PayPalOrApplePay from "@/components/ui/PaymentMethodsButtons/PayPayOrApplePay";
+
+export enum EPaymentMethod {
+ CREDIT_CARD = "card",
+ PAYPAL_OR_APPLE_PAY = "payPalOrApplePay",
+}
+
+interface IPaymentMethod {
+ id: EPaymentMethod;
+ component: JSX.Element;
+}
+
+export const paymentMethods: IPaymentMethod[] = [
+ {
+ id: EPaymentMethod.PAYPAL_OR_APPLE_PAY,
+ component: ,
+ },
+ {
+ id: EPaymentMethod.CREDIT_CARD,
+ component: ,
+ },
+];
diff --git a/src/index.css b/src/index.css
index cec9a22..963cec1 100644
--- a/src/index.css
+++ b/src/index.css
@@ -22,7 +22,8 @@ h4 {
font-weight: 400;
}
-button,h4 {
+button,
+h4 {
font-size: 18px;
}
@@ -36,18 +37,109 @@ input {
outline: none;
}
-a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video {
+a,
+abbr,
+acronym,
+address,
+applet,
+article,
+aside,
+audio,
+b,
+big,
+blockquote,
+body,
+canvas,
+caption,
+center,
+cite,
+code,
+dd,
+del,
+details,
+dfn,
+div,
+dl,
+dt,
+em,
+embed,
+fieldset,
+figcaption,
+figure,
+footer,
+form,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+header,
+hgroup,
+html,
+i,
+iframe,
+img,
+ins,
+kbd,
+label,
+legend,
+li,
+mark,
+menu,
+nav,
+object,
+ol,
+output,
+p,
+pre,
+q,
+ruby,
+s,
+samp,
+section,
+small,
+span,
+strike,
+strong,
+sub,
+summary,
+sup,
+table,
+tbody,
+td,
+tfoot,
+th,
+thead,
+time,
+tr,
+tt,
+u,
+ul,
+var,
+video {
border: 0;
margin: 0;
padding: 0;
vertical-align: initial;
}
-article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section {
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
display: block;
}
-body,html {
+body,
+html {
height: 100%;
line-height: 1;
}
@@ -56,7 +148,8 @@ body,html {
height: auto;
}
-ol,ul {
+ol,
+ul {
list-style: none;
}
@@ -65,8 +158,8 @@ a {
text-decoration: none;
}
-div[class^=divider] {
- font-family: SF Pro Text Regular,sans-serif
+div[class^="divider"] {
+ font-family: SF Pro Text Regular, sans-serif;
}
* {
@@ -93,7 +186,12 @@ div[class^=divider] {
height: 100%;
}
-a,button,div,input,select,textarea {
+a,
+button,
+div,
+input,
+select,
+textarea {
-webkit-tap-highlight-color: transparent;
}
@@ -134,4 +232,9 @@ a,button,div,input,select,textarea {
left: 0;
top: 0;
width: 100%;
-}
\ No newline at end of file
+}
+
+.no-scroll {
+ position: fixed;
+ overflow: hidden;
+}
diff --git a/src/init.tsx b/src/init.tsx
index ab725b2..c8573fb 100755
--- a/src/init.tsx
+++ b/src/init.tsx
@@ -37,13 +37,14 @@ const init = async () => {
.init(options);
window.Chargebee.init(config.chargebee);
+ const isProduction = import.meta.env.MODE === "production";
+
// SCRIPTS TO HEAD
const yandexMetric = () => {
const script = document.createElement("script");
script.setAttribute("src", "/metrics/yandex.js");
document.head.appendChild(script);
};
- yandexMetric();
const smartLook = () => {
if (!config.smartlook_manage) return;
@@ -51,7 +52,10 @@ const init = async () => {
script.setAttribute("src", "/metrics/smartlook.js");
document.head.appendChild(script);
};
- smartLook();
+ if (isProduction) {
+ yandexMetric();
+ smartLook();
+ }
// const googleManager = () => {
// const script = document.createElement("script");
@@ -59,7 +63,7 @@ const init = async () => {
// document.head.appendChild(script);
// };
// googleManager();
-
+
return (
diff --git a/src/routes.ts b/src/routes.ts
index b443700..7b67cfb 100755
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -74,6 +74,9 @@ const routes = {
onboarding: () => [host, "onboarding"].join("/"),
trialChoice: () => [host, "trial-choice"].join("/"),
trialPayment: () => [host, "trial-payment"].join("/"),
+ additionalDiscount: () => [host, "additional-discount"].join("/"),
+ trialPaymentWithDiscount: () =>
+ [host, "trial-payment-with-discount"].join("/"),
notFound: () => [host, "404"].join("/"),
},
server: {
@@ -221,6 +224,8 @@ export const withoutFooterRoutes = [
routes.client.onboarding(),
routes.client.trialChoice(),
routes.client.trialPayment(),
+ routes.client.additionalDiscount(),
+ routes.client.trialPaymentWithDiscount(),
];
export const withoutFooterPartOfRoutes = [routes.client.questionnaire()];
@@ -281,6 +286,8 @@ export const withoutHeaderRoutes = [
routes.client.satisfiedResult(),
routes.client.onboarding(),
routes.client.trialPayment(),
+ routes.client.additionalDiscount(),
+ routes.client.trialPaymentWithDiscount(),
];
export const hasNoHeader = (path: string) => {
return !withoutHeaderRoutes.includes(`/${path.split("/")[1]}`);
diff --git a/src/store/index.ts b/src/store/index.ts
index e06ad61..68cdcd6 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -4,7 +4,10 @@ import {
createAction,
} from "@reduxjs/toolkit";
import token, { actions as tokenActions, selectToken } from "./token";
-import questionnaire, { actions as questionnaireActions, selectQuestionnaire } from "./questionnaire";
+import questionnaire, {
+ actions as questionnaireActions,
+ selectQuestionnaire,
+} from "./questionnaire";
import user, { actions as userActions, selectUser } from "./user";
import form, {
actions as formActions,
@@ -27,6 +30,7 @@ import payment, {
actions as paymentActions,
selectActiveSubPlan,
selectIsDiscount,
+ selectSubscriptionReceipt,
} from "./payment";
import subscriptionPlans, {
actions as subscriptionPlasActions,
@@ -85,6 +89,7 @@ export const selectors = {
selectUserCallbacksPrevStat,
selectHome,
selectIsDiscount,
+ selectSubscriptionReceipt,
selectOnboarding,
selectOnboardingHome,
selectOnboardingCompatibility,
@@ -108,7 +113,7 @@ export const reducer = combineReducers({
userCallbacks,
siteConfig,
onboardingConfig,
- questionnaire
+ questionnaire,
});
export type RootState = ReturnType;
diff --git a/src/store/payment.ts b/src/store/payment.ts
index 908ad06..2f7631e 100644
--- a/src/store/payment.ts
+++ b/src/store/payment.ts
@@ -1,4 +1,5 @@
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
+import { SubscriptionReceipt } from "@/api/resources/UserSubscriptionReceipts";
import { createSlice, createSelector } from "@reduxjs/toolkit";
import type { PayloadAction } from "@reduxjs/toolkit";
@@ -6,12 +7,14 @@ interface IPayment {
selectedPrice: number | null;
isDiscount: boolean;
activeSubPlan: ISubscriptionPlan | null;
+ subscriptionReceipt: SubscriptionReceipt | null;
}
const initialState: IPayment = {
selectedPrice: null,
isDiscount: false,
- activeSubPlan: null
+ activeSubPlan: null,
+ subscriptionReceipt: null,
};
const paymentSlice = createSlice({
@@ -38,4 +41,8 @@ export const selectIsDiscount = createSelector(
(state: { payment: IPayment }) => state.payment.isDiscount,
(payment) => payment
);
+export const selectSubscriptionReceipt = createSelector(
+ (state: { payment: IPayment }) => state.payment.subscriptionReceipt,
+ (payment) => payment
+);
export default paymentSlice.reducer;