Merge branch 'develop' into 'main'
Develop See merge request witapp/aura-webapp!109
This commit is contained in:
commit
062bea2f7f
@ -19,17 +19,9 @@ export interface StripeReceiptPayload extends AuthPayload {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PayPalReceiptPayload extends AuthPayload {
|
|
||||||
subscription_receipt: {
|
|
||||||
sub_plan_id: string;
|
|
||||||
};
|
|
||||||
way: "paypal";
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Payload =
|
export type Payload =
|
||||||
| AppleReceiptPayload
|
| AppleReceiptPayload
|
||||||
| StripeReceiptPayload
|
| StripeReceiptPayload
|
||||||
| PayPalReceiptPayload;
|
|
||||||
|
|
||||||
export interface Response {
|
export interface Response {
|
||||||
subscription_receipt: SubscriptionReceipt;
|
subscription_receipt: SubscriptionReceipt;
|
||||||
@ -58,19 +50,12 @@ export interface SubscriptionReceipt {
|
|||||||
app_bundle_id: string;
|
app_bundle_id: string;
|
||||||
autorenewable: boolean;
|
autorenewable: boolean;
|
||||||
error: string;
|
error: string;
|
||||||
links?: IPayPalLink[];
|
|
||||||
stripe_status?: string;
|
stripe_status?: string;
|
||||||
checkout_url?: string;
|
checkout_url?: string;
|
||||||
checkout_session?: unknown;
|
checkout_session?: unknown;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPayPalLink {
|
|
||||||
href: string;
|
|
||||||
rel: "approve" | "edit" | "self";
|
|
||||||
method: "GET" | "PATCH";
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRequest({
|
function createRequest({
|
||||||
token,
|
token,
|
||||||
receiptData,
|
receiptData,
|
||||||
@ -101,14 +86,6 @@ function getDataPayload(payload: Payload) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if ("way" in payload && payload.way === "paypal") {
|
|
||||||
return {
|
|
||||||
way: "paypal",
|
|
||||||
subscription_receipt: {
|
|
||||||
sub_plan_id: payload.subscription_receipt.sub_plan_id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if ("way" in payload && payload.way === "stripe") {
|
if ("way" in payload && payload.way === "stripe") {
|
||||||
return {
|
return {
|
||||||
way: "stripe",
|
way: "stripe",
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import MainButton from "@/components/MainButton";
|
|
||||||
|
|
||||||
interface IPayPalButtonProps {
|
|
||||||
onClick: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PayPalButton({ onClick }: IPayPalButtonProps): JSX.Element {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<MainButton color="blue" onClick={onClick}>
|
|
||||||
{t("payPal")}
|
|
||||||
</MainButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -14,7 +14,6 @@ import ApplePayButton from "@/components/StripePage/ApplePayButton";
|
|||||||
import SubPlanInformation from "@/components/SubPlanInformation";
|
import SubPlanInformation from "@/components/SubPlanInformation";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
|
|
||||||
interface StripeModalProps {
|
interface StripeModalProps {
|
||||||
@ -39,7 +38,6 @@ StripeModalProps): JSX.Element {
|
|||||||
const email = useSelector(selectors.selectUser).email;
|
const email = useSelector(selectors.selectUser).email;
|
||||||
const [stripePromise, setStripePromise] =
|
const [stripePromise, setStripePromise] =
|
||||||
useState<Promise<Stripe | null> | null>(null);
|
useState<Promise<Stripe | null> | null>(null);
|
||||||
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[] | null>(null);
|
|
||||||
const [clientSecret, setClientSecret] = useState<string>("");
|
const [clientSecret, setClientSecret] = useState<string>("");
|
||||||
const [subscriptionReceiptId, setSubscriptionReceiptId] =
|
const [subscriptionReceiptId, setSubscriptionReceiptId] =
|
||||||
useState<string>("");
|
useState<string>("");
|
||||||
@ -53,7 +51,6 @@ StripeModalProps): JSX.Element {
|
|||||||
const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
|
const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
|
||||||
setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
|
setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
|
||||||
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
||||||
setSubPlans(sub_plans);
|
|
||||||
const isActiveSubPlan = sub_plans.find(
|
const isActiveSubPlan = sub_plans.find(
|
||||||
(subPlan) => subPlan.id === activeSubPlan?.id
|
(subPlan) => subPlan.id === activeSubPlan?.id
|
||||||
);
|
);
|
||||||
@ -78,6 +75,7 @@ StripeModalProps): JSX.Element {
|
|||||||
setClientSecret(client_secret);
|
setClientSecret(client_secret);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
})();
|
})();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [api, token]);
|
}, [api, token]);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
@ -107,7 +105,7 @@ StripeModalProps): JSX.Element {
|
|||||||
subscriptionReceiptId={subscriptionReceiptId}
|
subscriptionReceiptId={subscriptionReceiptId}
|
||||||
/>
|
/>
|
||||||
{activeSubPlan && (
|
{activeSubPlan && (
|
||||||
<SubPlanInformation subPlan={activeSubPlan} subPlans={subPlans} />
|
<SubPlanInformation subPlan={activeSubPlan} />
|
||||||
)}
|
)}
|
||||||
<CheckoutForm subscriptionReceiptId={subscriptionReceiptId} />
|
<CheckoutForm subscriptionReceiptId={subscriptionReceiptId} />
|
||||||
</Elements>
|
</Elements>
|
||||||
|
|||||||
@ -16,6 +16,7 @@ interface ApplePayButtonProps {
|
|||||||
client_secret: string;
|
client_secret: string;
|
||||||
subscriptionReceiptId?: string;
|
subscriptionReceiptId?: string;
|
||||||
returnUrl?: string;
|
returnUrl?: string;
|
||||||
|
setCanMakePayment?: (isCanMakePayment: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ApplePayButton({
|
function ApplePayButton({
|
||||||
@ -23,6 +24,7 @@ function ApplePayButton({
|
|||||||
client_secret,
|
client_secret,
|
||||||
subscriptionReceiptId,
|
subscriptionReceiptId,
|
||||||
returnUrl,
|
returnUrl,
|
||||||
|
setCanMakePayment,
|
||||||
}: ApplePayButtonProps) {
|
}: ApplePayButtonProps) {
|
||||||
const stripe = useStripe();
|
const stripe = useStripe();
|
||||||
const elements = useElements();
|
const elements = useElements();
|
||||||
@ -58,6 +60,7 @@ function ApplePayButton({
|
|||||||
pr.canMakePayment().then((result) => {
|
pr.canMakePayment().then((result) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
setPaymentRequest(pr);
|
setPaymentRequest(pr);
|
||||||
|
setCanMakePayment?.(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -80,7 +83,8 @@ function ApplePayButton({
|
|||||||
return e.complete("fail");
|
return e.complete("fail");
|
||||||
}
|
}
|
||||||
navigate(
|
navigate(
|
||||||
returnUrl || `${routes.client.paymentResult()}/${subscriptionReceiptId}/?redirect_status=succeeded`
|
returnUrl ||
|
||||||
|
`${routes.client.paymentResult()}/${subscriptionReceiptId}/?redirect_status=succeeded`
|
||||||
);
|
);
|
||||||
e.complete("success");
|
e.complete("success");
|
||||||
// Show a success message to your customer
|
// Show a success message to your customer
|
||||||
@ -89,6 +93,7 @@ function ApplePayButton({
|
|||||||
// payment_intent.succeeded event that handles any business critical
|
// payment_intent.succeeded event that handles any business critical
|
||||||
// post-payment actions.
|
// post-payment actions.
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
activeSubPlan,
|
activeSubPlan,
|
||||||
client_secret,
|
client_secret,
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import routes from "@/routes";
|
|||||||
import SubPlanInformation from "../SubPlanInformation";
|
import SubPlanInformation from "../SubPlanInformation";
|
||||||
import Title from "../Title";
|
import Title from "../Title";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
|
||||||
import ApplePayButton from "./ApplePayButton";
|
import ApplePayButton from "./ApplePayButton";
|
||||||
|
|
||||||
export function StripePage(): JSX.Element {
|
export function StripePage(): JSX.Element {
|
||||||
@ -26,7 +25,6 @@ export function StripePage(): JSX.Element {
|
|||||||
const email = useSelector(selectors.selectUser).email;
|
const email = useSelector(selectors.selectUser).email;
|
||||||
const [stripePromise, setStripePromise] =
|
const [stripePromise, setStripePromise] =
|
||||||
useState<Promise<Stripe | null> | null>(null);
|
useState<Promise<Stripe | null> | null>(null);
|
||||||
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[] | null>(null);
|
|
||||||
const [clientSecret, setClientSecret] = useState<string>("");
|
const [clientSecret, setClientSecret] = useState<string>("");
|
||||||
const [subscriptionReceiptId, setSubscriptionReceiptId] =
|
const [subscriptionReceiptId, setSubscriptionReceiptId] =
|
||||||
useState<string>("");
|
useState<string>("");
|
||||||
@ -40,7 +38,6 @@ export function StripePage(): JSX.Element {
|
|||||||
const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
|
const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
|
||||||
setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
|
setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
|
||||||
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
||||||
setSubPlans(sub_plans);
|
|
||||||
const isActiveSubPlan = sub_plans.find(
|
const isActiveSubPlan = sub_plans.find(
|
||||||
(subPlan) => subPlan.id === activeSubPlan?.id
|
(subPlan) => subPlan.id === activeSubPlan?.id
|
||||||
);
|
);
|
||||||
@ -65,6 +62,7 @@ export function StripePage(): JSX.Element {
|
|||||||
setClientSecret(client_secret);
|
setClientSecret(client_secret);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
})();
|
})();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [api, token]);
|
}, [api, token]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -90,7 +88,7 @@ export function StripePage(): JSX.Element {
|
|||||||
subscriptionReceiptId={subscriptionReceiptId}
|
subscriptionReceiptId={subscriptionReceiptId}
|
||||||
/>
|
/>
|
||||||
{activeSubPlan && (
|
{activeSubPlan && (
|
||||||
<SubPlanInformation subPlan={activeSubPlan} subPlans={subPlans} />
|
<SubPlanInformation subPlan={activeSubPlan} />
|
||||||
)}
|
)}
|
||||||
<CheckoutForm subscriptionReceiptId={subscriptionReceiptId} />
|
<CheckoutForm subscriptionReceiptId={subscriptionReceiptId} />
|
||||||
</Elements>
|
</Elements>
|
||||||
|
|||||||
@ -2,16 +2,10 @@ import { useTranslation } from "react-i18next";
|
|||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
||||||
import TotalToday from "./TotalToday";
|
import TotalToday from "./TotalToday";
|
||||||
import MainButton from "../MainButton";
|
|
||||||
import { useApi } from "@/api";
|
|
||||||
import { useAuth } from "@/auth";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import Loader from "../Loader";
|
|
||||||
import ApplePayButton from "../StripePage/ApplePayButton";
|
import ApplePayButton from "../StripePage/ApplePayButton";
|
||||||
|
|
||||||
interface ISubPlanInformationProps {
|
interface ISubPlanInformationProps {
|
||||||
subPlan: ISubscriptionPlan;
|
subPlan: ISubscriptionPlan;
|
||||||
subPlans: ISubscriptionPlan[] | null;
|
|
||||||
client_secret?: string;
|
client_secret?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,58 +17,9 @@ const getPrice = (plan: ISubscriptionPlan): string => {
|
|||||||
|
|
||||||
function SubPlanInformation({
|
function SubPlanInformation({
|
||||||
subPlan,
|
subPlan,
|
||||||
subPlans,
|
|
||||||
client_secret,
|
client_secret,
|
||||||
}: ISubPlanInformationProps): JSX.Element {
|
}: ISubPlanInformationProps): JSX.Element {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const api = useApi();
|
|
||||||
const { token } = useAuth();
|
|
||||||
const [payPalSubPlan, setPayPalSubPlan] = useState<ISubscriptionPlan>();
|
|
||||||
const [errors, setErrors] = useState<string>("");
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!subPlans) return;
|
|
||||||
const paypalPlan = subPlans
|
|
||||||
.filter((plan: ISubscriptionPlan) => plan.provider === "paypal")
|
|
||||||
.filter((plan: ISubscriptionPlan) => {
|
|
||||||
if (subPlan?.trial && plan?.trial) return true;
|
|
||||||
if (!subPlan?.trial && !plan?.trial) return true;
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.find((plan: ISubscriptionPlan) => {
|
|
||||||
if (subPlan?.trial && plan?.trial) {
|
|
||||||
return plan?.trial?.price_cents === subPlan?.trial?.price_cents;
|
|
||||||
}
|
|
||||||
if (!subPlan?.trial && !plan?.trial) {
|
|
||||||
return plan?.name === subPlan?.name;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
setPayPalSubPlan(paypalPlan);
|
|
||||||
}, [subPlan?.name, subPlan?.trial, subPlans]);
|
|
||||||
|
|
||||||
const handlePayPalButton = async () => {
|
|
||||||
setIsLoading(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.");
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
window.location.href = link.href;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
@ -82,21 +27,9 @@ function SubPlanInformation({
|
|||||||
{client_secret && (
|
{client_secret && (
|
||||||
<ApplePayButton activeSubPlan={subPlan} client_secret={client_secret} />
|
<ApplePayButton activeSubPlan={subPlan} client_secret={client_secret} />
|
||||||
)}
|
)}
|
||||||
{payPalSubPlan && (
|
|
||||||
<MainButton
|
|
||||||
type="button"
|
|
||||||
className={styles["pay-pal-button"]}
|
|
||||||
onClick={handlePayPalButton}
|
|
||||||
>
|
|
||||||
{!isLoading && <img src="/paypal-logo.svg" alt="PayPal Button" />}
|
|
||||||
{isLoading && <Loader />}
|
|
||||||
</MainButton>
|
|
||||||
)}
|
|
||||||
{/* <ApplePayButton activeSubPlan={subPlan} /> */}
|
|
||||||
<p className={styles.description}>
|
<p className={styles.description}>
|
||||||
{t("auweb.pay.information").replaceAll("%@", getPrice(subPlan))}.
|
{t("auweb.pay.information").replaceAll("%@", getPrice(subPlan))}.
|
||||||
</p>
|
</p>
|
||||||
{!!errors.length && <p className={styles.errors}>{errors}</p>}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
|
import { EPaymentMethod, IPaymentMethod } from "@/data/paymentMethods";
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
|
|
||||||
interface IPaymentMethodsChoiceProps {
|
interface IPaymentMethodsChoiceProps {
|
||||||
selectedPaymentMethod: EPaymentMethod;
|
selectedPaymentMethod: EPaymentMethod;
|
||||||
onSelectPaymentMethod: (method: EPaymentMethod) => void;
|
onSelectPaymentMethod: (method: EPaymentMethod) => void;
|
||||||
|
paymentMethods: IPaymentMethod[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function PaymentMethodsChoice({
|
function PaymentMethodsChoice({
|
||||||
selectedPaymentMethod,
|
selectedPaymentMethod,
|
||||||
|
paymentMethods,
|
||||||
onSelectPaymentMethod,
|
onSelectPaymentMethod,
|
||||||
}: IPaymentMethodsChoiceProps) {
|
}: IPaymentMethodsChoiceProps) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.payment-methods {
|
.payment-methods {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-around;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
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 (
|
|
||||||
<MainButton
|
|
||||||
type="button"
|
|
||||||
className={styles["pay-pal-button"]}
|
|
||||||
onClick={handlePayPalButton}
|
|
||||||
>
|
|
||||||
{!isLoading && (
|
|
||||||
<div className={styles.content}>
|
|
||||||
<img src="/paypal-logo.svg" alt="PayPal Button" />
|
|
||||||
<p>Buy Now</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isLoading && <Loader />}
|
|
||||||
</MainButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PayPalButton;
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
.pay-pal-button {
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: #ffc43a;
|
|
||||||
border-radius: 7px;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content > p {
|
|
||||||
margin-top: 6px;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #2f2e37;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content > p {
|
|
||||||
margin-top: 6px;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #2f2e37;
|
|
||||||
}
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import Title from "@/components/Title";
|
import Title from "@/components/Title";
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
import PaymentMethodsChoice from "../PaymentMethodsChoice";
|
import PaymentMethodsChoice from "../PaymentMethodsChoice";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { EPaymentMethod } from "@/data/paymentMethods";
|
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
|
||||||
import { Elements } from "@stripe/react-stripe-js";
|
import { Elements } from "@stripe/react-stripe-js";
|
||||||
import ApplePayButton from "@/components/StripePage/ApplePayButton";
|
import ApplePayButton from "@/components/StripePage/ApplePayButton";
|
||||||
import CheckoutForm from "@/components/PaymentPage/methods/Stripe/CheckoutForm";
|
import CheckoutForm from "@/components/PaymentPage/methods/Stripe/CheckoutForm";
|
||||||
@ -18,7 +18,6 @@ import { useAuth } from "@/auth";
|
|||||||
import Loader from "@/components/Loader";
|
import Loader from "@/components/Loader";
|
||||||
import { getPriceFromTrial } from "@/services/price";
|
import { getPriceFromTrial } from "@/services/price";
|
||||||
import SecurityPayments from "../SecurityPayments";
|
import SecurityPayments from "../SecurityPayments";
|
||||||
import PayPalButton from "./components/PayPalButton";
|
|
||||||
|
|
||||||
interface IPaymentModalProps {
|
interface IPaymentModalProps {
|
||||||
activeSubscriptionPlan?: ISubscriptionPlan;
|
activeSubscriptionPlan?: ISubscriptionPlan;
|
||||||
@ -26,7 +25,11 @@ interface IPaymentModalProps {
|
|||||||
returnUrl?: string;
|
returnUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentModalProps) {
|
function PaymentModal({
|
||||||
|
activeSubscriptionPlan,
|
||||||
|
noTrial,
|
||||||
|
returnUrl,
|
||||||
|
}: IPaymentModalProps) {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const locale = i18n.language;
|
const locale = i18n.language;
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
@ -36,20 +39,23 @@ function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentMo
|
|||||||
const activeSubPlan = activeSubscriptionPlan
|
const activeSubPlan = activeSubscriptionPlan
|
||||||
? activeSubscriptionPlan
|
? activeSubscriptionPlan
|
||||||
: activeSubPlanFromStore;
|
: activeSubPlanFromStore;
|
||||||
const [payPalSubPlan, setPayPalSubPlan] = useState<ISubscriptionPlan>();
|
|
||||||
const [stripePromise, setStripePromise] =
|
const [stripePromise, setStripePromise] =
|
||||||
useState<Promise<Stripe | null> | null>(null);
|
useState<Promise<Stripe | null> | null>(null);
|
||||||
const [clientSecret, setClientSecret] = useState<string>("");
|
const [clientSecret, setClientSecret] = useState<string>("");
|
||||||
const [subscriptionReceiptId, setSubscriptionReceiptId] =
|
const [subscriptionReceiptId, setSubscriptionReceiptId] =
|
||||||
useState<string>("");
|
useState<string>("");
|
||||||
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[] | null>(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isLoadingPayPal, setIsLoadingPayPal] = useState(false);
|
|
||||||
const [errors, setErrors] = useState<string>("");
|
|
||||||
const [isError, setIsError] = useState<boolean>(false);
|
const [isError, setIsError] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const paymentMethodsButtons = useMemo(() => {
|
||||||
|
// return paymentMethods.filter(
|
||||||
|
// (method) => method.id !== EPaymentMethod.PAYMENT_BUTTONS
|
||||||
|
// );
|
||||||
|
return paymentMethods;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(
|
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(
|
||||||
EPaymentMethod.PAYPAL_OR_APPLE_PAY
|
EPaymentMethod.PAYMENT_BUTTONS
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSelectPaymentMethod = (method: EPaymentMethod) => {
|
const onSelectPaymentMethod = (method: EPaymentMethod) => {
|
||||||
@ -59,12 +65,8 @@ function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentMo
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
|
const siteConfig = await api.getAppConfig({ bundleId: "auraweb" });
|
||||||
// const isProduction = import.meta.env.MODE === "production";
|
|
||||||
// const stripePublicKey = isProduction ? siteConfig.data.stripe_public_key : "pk_test_51Ndqf4IlX4lgwUxrlLWqfYWpo0Ic0BV7DfiZxfMYy838IZP8NLrwwZ5i0HhhbOQBGoQZe4Rrel1ziEk8mhQ2TE3500ETWZPBva";
|
|
||||||
// setStripePromise(loadStripe(stripePublicKey));
|
|
||||||
setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
|
setStripePromise(loadStripe(siteConfig.data.stripe_public_key));
|
||||||
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
||||||
setSubPlans(sub_plans);
|
|
||||||
const isActiveSubPlan = sub_plans.find(
|
const isActiveSubPlan = sub_plans.find(
|
||||||
(subPlan) => subPlan.id === activeSubPlan?.id
|
(subPlan) => subPlan.id === activeSubPlan?.id
|
||||||
);
|
);
|
||||||
@ -100,50 +102,6 @@ function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentMo
|
|||||||
})();
|
})();
|
||||||
}, [activeSubPlan?.id, api, token]);
|
}, [activeSubPlan?.id, api, 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;
|
|
||||||
// window.open(link.href, '_blank');
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className={styles["payment-modal"]}>
|
<div className={styles["payment-modal"]}>
|
||||||
@ -170,6 +128,7 @@ function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentMo
|
|||||||
Choose payment method
|
Choose payment method
|
||||||
</Title>
|
</Title>
|
||||||
<PaymentMethodsChoice
|
<PaymentMethodsChoice
|
||||||
|
paymentMethods={paymentMethodsButtons}
|
||||||
selectedPaymentMethod={selectedPaymentMethod}
|
selectedPaymentMethod={selectedPaymentMethod}
|
||||||
onSelectPaymentMethod={onSelectPaymentMethod}
|
onSelectPaymentMethod={onSelectPaymentMethod}
|
||||||
/>
|
/>
|
||||||
@ -180,7 +139,8 @@ function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentMo
|
|||||||
<p className={styles["sub-plan-description"]}>
|
<p className={styles["sub-plan-description"]}>
|
||||||
You will be charged only{" "}
|
You will be charged only{" "}
|
||||||
<b>
|
<b>
|
||||||
${getPriceFromTrial(activeSubPlan?.trial)} for your 3-day trial.
|
${getPriceFromTrial(activeSubPlan?.trial)} for your 3-day
|
||||||
|
trial.
|
||||||
</b>
|
</b>
|
||||||
</p>
|
</p>
|
||||||
<p className={styles["sub-plan-description"]}>
|
<p className={styles["sub-plan-description"]}>
|
||||||
@ -197,26 +157,22 @@ function PaymentModal({ activeSubscriptionPlan, noTrial, returnUrl }: IPaymentMo
|
|||||||
<div className={styles["payment-method-container"]}>
|
<div className={styles["payment-method-container"]}>
|
||||||
{stripePromise && clientSecret && (
|
{stripePromise && clientSecret && (
|
||||||
<Elements stripe={stripePromise} options={{ clientSecret }}>
|
<Elements stripe={stripePromise} options={{ clientSecret }}>
|
||||||
{selectedPaymentMethod === EPaymentMethod.PAYPAL_OR_APPLE_PAY && (
|
{selectedPaymentMethod === EPaymentMethod.PAYMENT_BUTTONS && (
|
||||||
<div className={styles["payment-method"]}>
|
<div className={styles["payment-method"]}>
|
||||||
{payPalSubPlan && (
|
|
||||||
<PayPalButton
|
|
||||||
isLoading={isLoadingPayPal}
|
|
||||||
handlePayPalButton={handlePayPalButton}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<ApplePayButton
|
<ApplePayButton
|
||||||
activeSubPlan={activeSubPlan}
|
activeSubPlan={activeSubPlan}
|
||||||
client_secret={clientSecret}
|
client_secret={clientSecret}
|
||||||
subscriptionReceiptId={subscriptionReceiptId}
|
subscriptionReceiptId={subscriptionReceiptId}
|
||||||
returnUrl={window.location.href}
|
returnUrl={window.location.href}
|
||||||
/>
|
/>
|
||||||
{!!errors.length && <p className={styles.errors}>{errors}</p>}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedPaymentMethod === EPaymentMethod.CREDIT_CARD && (
|
{selectedPaymentMethod === EPaymentMethod.CREDIT_CARD && (
|
||||||
<CheckoutForm subscriptionReceiptId={subscriptionReceiptId} returnUrl={returnUrl} />
|
<CheckoutForm
|
||||||
|
subscriptionReceiptId={subscriptionReceiptId}
|
||||||
|
returnUrl={returnUrl}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Elements>
|
</Elements>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
.payment-method-container {
|
.payment-method-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.address {
|
.address {
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from "react";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
import { selectors } from "@/store";
|
import { selectors } from "@/store";
|
||||||
import useSteps, { Step } from '@/hooks/palmistry/use-steps';
|
import useSteps, { Step } from "@/hooks/palmistry/use-steps";
|
||||||
import Button from '@/components/palmistry/button/button';
|
import Button from "@/components/palmistry/button/button";
|
||||||
import EmailHeader from '@/components/palmistry/email-header/email-header';
|
import EmailHeader from "@/components/palmistry/email-header/email-header";
|
||||||
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
||||||
import { actions } from "@/store";
|
import { actions } from "@/store";
|
||||||
import { useApi } from "@/api";
|
import { useApi } from "@/api";
|
||||||
|
|
||||||
const bestPlanId = 'stripe.15';
|
const bestPlanId = "stripe.15";
|
||||||
|
|
||||||
const getFormattedPrice = (plan: ISubscriptionPlan) => {
|
const getFormattedPrice = (plan: ISubscriptionPlan) => {
|
||||||
return (plan.trial!.price_cents / 100).toFixed(2);
|
return (plan.trial!.price_cents / 100).toFixed(2);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function StepSubscriptionPlan() {
|
export default function StepSubscriptionPlan() {
|
||||||
const steps = useSteps();
|
const steps = useSteps();
|
||||||
@ -23,11 +23,14 @@ export default function StepSubscriptionPlan() {
|
|||||||
const api = useApi();
|
const api = useApi();
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan);
|
const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan);
|
||||||
|
const allowedPlans = useMemo(() => ["stripe.37"], []);
|
||||||
|
|
||||||
const storedEmail = steps.getStoredValue(Step.Email);
|
const storedEmail = steps.getStoredValue(Step.Email);
|
||||||
|
|
||||||
const [subscriptionPlan, setSubscriptionPlan] = React.useState('');
|
const [subscriptionPlan, setSubscriptionPlan] = React.useState("");
|
||||||
const [subscriptionPlans, setSubscriptionPlans] = React.useState<ISubscriptionPlan[]>([]);
|
const [subscriptionPlans, setSubscriptionPlans] = React.useState<
|
||||||
|
ISubscriptionPlan[]
|
||||||
|
>([]);
|
||||||
const [email, setEmail] = React.useState(steps.getStoredValue(Step.Email));
|
const [email, setEmail] = React.useState(steps.getStoredValue(Step.Email));
|
||||||
|
|
||||||
const locale = i18n.language;
|
const locale = i18n.language;
|
||||||
@ -42,7 +45,10 @@ export default function StepSubscriptionPlan() {
|
|||||||
(async () => {
|
(async () => {
|
||||||
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
const { sub_plans } = await api.getSubscriptionPlans({ locale });
|
||||||
const plans = sub_plans
|
const plans = sub_plans
|
||||||
.filter((plan: ISubscriptionPlan) => plan.provider === "stripe")
|
.filter(
|
||||||
|
(plan: ISubscriptionPlan) =>
|
||||||
|
plan.provider === "stripe" && !plan.name.includes("(test)")
|
||||||
|
)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (!a.trial || !b.trial) {
|
if (!a.trial || !b.trial) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -55,13 +61,19 @@ export default function StepSubscriptionPlan() {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
setSubscriptionPlans(plans.filter((plan) => plan.trial?.price_cents));
|
setSubscriptionPlans(
|
||||||
|
plans.filter(
|
||||||
|
(plan) => plan.trial?.price_cents || allowedPlans.includes(plan.id)
|
||||||
|
)
|
||||||
|
);
|
||||||
})();
|
})();
|
||||||
}, [api, locale]);
|
}, [allowedPlans, api, locale]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (subscriptionPlan) {
|
if (subscriptionPlan) {
|
||||||
const targetSubPlan = subscriptionPlans.find((sub_plan) => sub_plan.id === subscriptionPlan);
|
const targetSubPlan = subscriptionPlans.find(
|
||||||
|
(sub_plan) => sub_plan.id === subscriptionPlan
|
||||||
|
);
|
||||||
|
|
||||||
if (targetSubPlan) {
|
if (targetSubPlan) {
|
||||||
dispatch(actions.payment.update({ activeSubPlan: targetSubPlan }));
|
dispatch(actions.payment.update({ activeSubPlan: targetSubPlan }));
|
||||||
@ -70,7 +82,7 @@ export default function StepSubscriptionPlan() {
|
|||||||
}, [subscriptionPlan]);
|
}, [subscriptionPlan]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setEmail(storedEmail || '');
|
setEmail(storedEmail || "");
|
||||||
}, [storedEmail]);
|
}, [storedEmail]);
|
||||||
|
|
||||||
const onNext = () => {
|
const onNext = () => {
|
||||||
@ -80,15 +92,21 @@ export default function StepSubscriptionPlan() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EmailHeader email={email}/>
|
<EmailHeader email={email} />
|
||||||
|
|
||||||
<div className="palmistry-container__title">
|
<div className="palmistry-container__title">
|
||||||
We've helped millions of people to reveal the destiny of their love life and what the future holds for them and
|
We've helped millions of people to reveal the destiny of their love life
|
||||||
their families.
|
and what the future holds for them and their families.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="palmistry-container__image">
|
<div className="palmistry-container__image">
|
||||||
<svg width="153" height="133" viewBox="0 0 153 133" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg
|
||||||
|
width="153"
|
||||||
|
height="133"
|
||||||
|
viewBox="0 0 153 133"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
<g clipPath="url(#clip0_16903_24136)">
|
<g clipPath="url(#clip0_16903_24136)">
|
||||||
<rect width="153" height="133" fill=""></rect>
|
<rect width="153" height="133" fill=""></rect>
|
||||||
<path
|
<path
|
||||||
@ -131,7 +149,11 @@ export default function StepSubscriptionPlan() {
|
|||||||
{subscriptionPlans.map((plan) => (
|
{subscriptionPlans.map((plan) => (
|
||||||
<div
|
<div
|
||||||
key={plan.id}
|
key={plan.id}
|
||||||
className={`palmistry-container__plan ${subscriptionPlan === plan.id ? 'palmistry-container__plan_active' : ''}`}
|
className={`palmistry-container__plan ${
|
||||||
|
subscriptionPlan === plan.id
|
||||||
|
? "palmistry-container__plan_active"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
onClick={() => setSubscriptionPlan(plan.id)}
|
onClick={() => setSubscriptionPlan(plan.id)}
|
||||||
>
|
>
|
||||||
<h3>${getFormattedPrice(plan)}</h3>
|
<h3>${getFormattedPrice(plan)}</h3>
|
||||||
@ -139,11 +161,23 @@ export default function StepSubscriptionPlan() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className={`palmistry-container__subscription-text ${subscriptionPlan === bestPlanId ? 'palmistry-container__subscription-text_active' : ''}`}>
|
<span
|
||||||
It costs us $13.21 to compensate our AURA employees for the trial, but please choose the amount you are comfortable with.
|
className={`palmistry-container__subscription-text ${
|
||||||
|
subscriptionPlan === bestPlanId
|
||||||
|
? "palmistry-container__subscription-text_active"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
It costs us $13.21 to compensate our AURA employees for the trial, but
|
||||||
|
please choose the amount you are comfortable with.
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Button className="palmistry-container__button" type="button" onClick={onNext} active>
|
<Button
|
||||||
|
className="palmistry-container__button"
|
||||||
|
type="button"
|
||||||
|
onClick={onNext}
|
||||||
|
active
|
||||||
|
>
|
||||||
Continue
|
Continue
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
|
|
||||||
function PayPalOrApplePay() {
|
function PaymentButtons() {
|
||||||
return <div className={styles.container}>
|
return <div className={styles.container}>
|
||||||
<img src="/paypal.webp" alt="PayPal" />
|
|
||||||
<img src="/applepay.webp" alt="ApplePay" />
|
<img src="/applepay.webp" alt="ApplePay" />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PayPalOrApplePay;
|
export default PaymentButtons;
|
||||||
@ -1,20 +1,20 @@
|
|||||||
import CreditCard from "@/components/ui/PaymentMethodsButtons/CreditCard";
|
import CreditCard from "@/components/ui/PaymentMethodsButtons/CreditCard";
|
||||||
import PayPalOrApplePay from "@/components/ui/PaymentMethodsButtons/PayPayOrApplePay";
|
import PaymentButtons from "@/components/ui/PaymentMethodsButtons/PaymentButtons";
|
||||||
|
|
||||||
export enum EPaymentMethod {
|
export enum EPaymentMethod {
|
||||||
CREDIT_CARD = "card",
|
CREDIT_CARD = "card",
|
||||||
PAYPAL_OR_APPLE_PAY = "payPalOrApplePay",
|
PAYMENT_BUTTONS = "paymentButtons",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPaymentMethod {
|
export interface IPaymentMethod {
|
||||||
id: EPaymentMethod;
|
id: EPaymentMethod;
|
||||||
component: JSX.Element;
|
component: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const paymentMethods: IPaymentMethod[] = [
|
export const paymentMethods: IPaymentMethod[] = [
|
||||||
{
|
{
|
||||||
id: EPaymentMethod.PAYPAL_OR_APPLE_PAY,
|
id: EPaymentMethod.PAYMENT_BUTTONS,
|
||||||
component: <PayPalOrApplePay />,
|
component: <PaymentButtons />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: EPaymentMethod.CREDIT_CARD,
|
id: EPaymentMethod.CREDIT_CARD,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user