payment-edits
edits
This commit is contained in:
parent
3f8538f8b5
commit
caf517d1f0
@ -1,11 +0,0 @@
|
||||
export default function PaymentLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<main className="p-4 pb-220 max-w-[560px] mx-auto relative min-h-dvh">
|
||||
{children}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
// import { getTranslations } from "next-intl/server";
|
||||
|
||||
import AnimatedInfoScreen from "@/components/widgets/AnimatedInfoScreen/AnimatedInfoScreen";
|
||||
import LottieAnimation from "@/components/widgets/LottieAnimation/LottieAnimation";
|
||||
import { ROUTES } from "@/shared/constants/client-routes";
|
||||
import { ELottieKeys } from "@/shared/constants/lottie";
|
||||
|
||||
export default async function PaymentFailed() {
|
||||
// const t = await getTranslations("Payment.Error");
|
||||
|
||||
return (
|
||||
<AnimatedInfoScreen
|
||||
lottieAnimation={
|
||||
<LottieAnimation loadKey={ELottieKeys.loaderCheckMark} />
|
||||
}
|
||||
// title={t("title")}
|
||||
title="Payment failed"
|
||||
animationTime={0}
|
||||
animationTexts={[]}
|
||||
buttonText="Try again"
|
||||
nextRoute={ROUTES.home()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { createPaymentCheckout } from "@/entities/payment/api";
|
||||
import { ROUTES } from "@/shared/constants/client-routes";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const productId = req.nextUrl.searchParams.get("productId");
|
||||
const placementId = req.nextUrl.searchParams.get("placementId");
|
||||
const paywallId = req.nextUrl.searchParams.get("paywallId");
|
||||
const fbPixels = req.nextUrl.searchParams.get("fb_pixels");
|
||||
const productPrice = req.nextUrl.searchParams.get("price");
|
||||
const currency = req.nextUrl.searchParams.get("currency");
|
||||
|
||||
console.log("PRODUCT ID:", productId);
|
||||
const data = await createPaymentCheckout({
|
||||
productId: productId || "",
|
||||
placementId: placementId || "",
|
||||
paywallId: paywallId || "",
|
||||
});
|
||||
console.log("DATA:", data);
|
||||
|
||||
let redirectUrl: URL = new URL(data?.paymentUrl || "", req.nextUrl.origin);
|
||||
if (!redirectUrl) {
|
||||
redirectUrl = new URL(`${ROUTES.paymentFailed()}`, origin);
|
||||
}
|
||||
if (fbPixels) redirectUrl.searchParams.set("fb_pixels", fbPixels);
|
||||
if (productPrice) redirectUrl.searchParams.set("price", productPrice);
|
||||
if (currency) redirectUrl.searchParams.set("currency", currency);
|
||||
|
||||
return NextResponse.redirect(redirectUrl, { status: 307 });
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ActionButton } from "@/components/ui/ActionButton/ActionButton";
|
||||
// import { useRouter } from "next/navigation";
|
||||
// import { useTranslations } from "next-intl";
|
||||
|
||||
import Typography from "@/components/ui/Typography/Typography";
|
||||
// import { ROUTES } from "@/shared/constants/client-routes";
|
||||
|
||||
// import styles from "./Button.module.scss";
|
||||
|
||||
export default function PaymentSuccessButton() {
|
||||
// const t = useTranslations("Payment.Success");
|
||||
// const router = useRouter();
|
||||
|
||||
const handleNext = () => {
|
||||
// router.push(ROUTES.additionalPurchases());
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
onClick={handleNext}
|
||||
className="fixed bottom-[calc(0dvh+64px)] left-1/2 -translate-x-1/2 opacity-0 pointer-events-none max-w-[400px] w-[calc(100dvw-32px)] [animation:fadeIn_0.5s_ease-in-out_forwards] [animation-delay:2s]"
|
||||
>
|
||||
<Typography color="primary" size="xl" weight="bold">
|
||||
{/* {t("button")} */}
|
||||
Done
|
||||
</Typography>
|
||||
</ActionButton>
|
||||
);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
// import { getTranslations } from "next-intl/server";
|
||||
|
||||
import AnimatedInfoScreen from "@/components/widgets/AnimatedInfoScreen/AnimatedInfoScreen";
|
||||
import LottieAnimation from "@/components/widgets/LottieAnimation/LottieAnimation";
|
||||
|
||||
import PaymentSuccessButton from "./Button";
|
||||
import { ELottieKeys } from "@/shared/constants/lottie";
|
||||
|
||||
export default async function PaymentSuccess() {
|
||||
// const t = await getTranslations("Payment.Success");
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatedInfoScreen
|
||||
lottieAnimation={
|
||||
<LottieAnimation loadKey={ELottieKeys.loaderCheckMark} />
|
||||
}
|
||||
// title={t("title")}
|
||||
title="Payment successful"
|
||||
/>
|
||||
<PaymentSuccessButton />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -37,8 +37,39 @@ import { usePaymentPlacement } from "@/hooks/payment/usePaymentPlacement";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { Currency } from "@/shared/types";
|
||||
import { getFormattedPrice } from "@/shared/utils/price";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ROUTES } from "@/shared/constants/client-routes";
|
||||
import { useClientToken } from "@/hooks/auth/useClientToken";
|
||||
|
||||
function getTrackingCookiesForRedirect() {
|
||||
const cookieObj = Object.fromEntries(
|
||||
document.cookie.split("; ").map((c) => c.split("="))
|
||||
);
|
||||
|
||||
const result = Object.entries(cookieObj).filter(([key]) => {
|
||||
return (
|
||||
[
|
||||
"_fbc",
|
||||
"_fbp",
|
||||
"_ym_uid",
|
||||
"_ym_d",
|
||||
"_ym_isad",
|
||||
"_ym_visorc",
|
||||
"yandexuid",
|
||||
"ymex",
|
||||
].includes(key) ||
|
||||
key.startsWith("_ga") ||
|
||||
key.startsWith("_gid")
|
||||
);
|
||||
});
|
||||
|
||||
const queryString = result
|
||||
.map(
|
||||
([key, value]) =>
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
|
||||
)
|
||||
.join("&");
|
||||
|
||||
return queryString;
|
||||
}
|
||||
|
||||
interface TrialPaymentTemplateProps {
|
||||
funnel: FunnelDefinition;
|
||||
@ -58,7 +89,7 @@ export function TrialPaymentTemplate({
|
||||
screenProgress,
|
||||
defaultTexts,
|
||||
}: TrialPaymentTemplateProps) {
|
||||
const router = useRouter();
|
||||
const token = useClientToken();
|
||||
|
||||
// TODO: выбрать корректный paymentId для этого экрана (ключ из backend), временно "main"
|
||||
const paymentId = "main";
|
||||
@ -76,17 +107,15 @@ export function TrialPaymentTemplate({
|
||||
const billingPeriod = placement?.billingPeriod;
|
||||
const billingInterval = placement?.billingInterval || 1;
|
||||
const currency = placement?.currency || Currency.USD;
|
||||
|
||||
console.log({ placement });
|
||||
const paymentUrl = placement?.paymentUrl || "";
|
||||
|
||||
const handlePayClick = () => {
|
||||
router.push(
|
||||
ROUTES.payment({
|
||||
productId,
|
||||
placementId,
|
||||
paywallId,
|
||||
})
|
||||
);
|
||||
const redirectUrl = `${paymentUrl}?paywallId=${paywallId}&placementId=${placementId}&productId=${productId}&jwtToken=${token}&price=${(
|
||||
(trialPrice || 100) / 100
|
||||
).toFixed(2)}¤cy=${currency}&${getTrackingCookiesForRedirect()}`;
|
||||
console.log("redirectUrl", redirectUrl);
|
||||
|
||||
// return window.location.replace(redirectUrl);
|
||||
};
|
||||
|
||||
const paymentSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
@ -215,7 +244,7 @@ export function TrialPaymentTemplate({
|
||||
}
|
||||
);
|
||||
|
||||
if (isLoading || !placement) {
|
||||
if (isLoading || !placement || !token) {
|
||||
return (
|
||||
<div className="w-full min-h-dvh max-w-[560px] mx-auto flex items-center justify-center">
|
||||
<Spinner className="size-8" />
|
||||
@ -239,7 +268,7 @@ export function TrialPaymentTemplate({
|
||||
{/* Header block */}
|
||||
{screen.headerBlock && (
|
||||
<Header
|
||||
className="mt-3"
|
||||
className="mt-3 sticky top-[18px] z-30"
|
||||
text={buildTypographyProps(screen.headerBlock.text, {
|
||||
as: "p",
|
||||
defaults: { font: "inter", weight: "semiBold", size: "sm" },
|
||||
|
||||
@ -29,8 +29,7 @@ const buttonVariants = cva(
|
||||
},
|
||||
active: {
|
||||
true: "bg-gradient-to-r from-[#EBF5FF] to-[#DBEAFE] border-primary shadow-blue-glow-2 text-primary",
|
||||
false:
|
||||
"bg-background border-border shadow-black-glow text-black",
|
||||
false: "bg-background border-border shadow-black-glow text-black",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@ -64,6 +63,16 @@ function MainButton({
|
||||
data-slot="main-button"
|
||||
className={cn(buttonVariants({ cornerRadius, active, className }))}
|
||||
{...props}
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (disabled) return;
|
||||
const targetEl = e.target as HTMLElement | null;
|
||||
const isCheckboxTarget = targetEl?.closest('[data-slot="checkbox"]');
|
||||
if (isCheckboxTarget) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
props.onClick?.(e);
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
<Label
|
||||
@ -81,7 +90,13 @@ function MainButton({
|
||||
{...checkboxProps}
|
||||
checked={active ?? false}
|
||||
disabled={disabled}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onClick={(e) => {
|
||||
if (disabled) return;
|
||||
e.stopPropagation();
|
||||
props.onClick?.(
|
||||
e as unknown as React.MouseEvent<HTMLButtonElement>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Label>
|
||||
|
||||
@ -76,7 +76,7 @@ const BottomActionButton = forwardRef<HTMLDivElement, BottomActionButtonProps>(
|
||||
<div
|
||||
ref={innerRef}
|
||||
className={cn(
|
||||
"fixed bottom-0 left-1/2 -translate-x-1/2 w-full",
|
||||
"fixed bottom-0 left-1/2 -translate-x-1/2 w-full z-10",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@ -12,3 +12,8 @@ export const setAuthTokenToCookie = async (token: string): Promise<void> => {
|
||||
maxAge: 60 * 60 * 24 * 365,
|
||||
});
|
||||
};
|
||||
|
||||
export const getAuthTokenFromCookie = async (): Promise<string | undefined> => {
|
||||
const cookieStore = await cookies();
|
||||
return cookieStore.get("accessToken")?.value;
|
||||
};
|
||||
|
||||
17
src/hooks/auth/useClientToken.ts
Normal file
17
src/hooks/auth/useClientToken.ts
Normal file
@ -0,0 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { getAuthTokenFromCookie } from "@/entities/user/serverActions";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
export const useClientToken = () => {
|
||||
const [token, setToken] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const token = await getAuthTokenFromCookie();
|
||||
setToken(token);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return useMemo(() => token, [token]);
|
||||
};
|
||||
@ -100,8 +100,10 @@ class HttpClient {
|
||||
accessToken = await getServerAccessToken();
|
||||
} else {
|
||||
try {
|
||||
const { getClientAccessToken } = await import("../auth/token");
|
||||
accessToken = getClientAccessToken();
|
||||
const { getAuthTokenFromCookie } = await import(
|
||||
"@/entities/user/serverActions"
|
||||
);
|
||||
accessToken = await getAuthTokenFromCookie();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user