Commit a15facb5 by Arjun Jhukal

updated the new functionality for acuity tech and removed age gate

parent ad8df29f
{
"postman.settings.dotenv-detection-notification-visibility": false
}
......@@ -3,9 +3,9 @@
"use client"
import GlassWrapper from '@/components/molecules/GlassWrapper'
import { useAppDispatch } from '@/hooks/hook'
import { useAppDispatch, useAppSelector } from '@/hooks/hook'
import { setTokens } from '@/slice/authSlice'
import { restoreAuthFromCookies } from '@/utils/authSession'
import { clearAuthBackup, restoreAuthFromCookies } from '@/utils/authSession'
import Image from 'next/image'
import Link from 'next/link'
import { useParams } from 'next/navigation'
......@@ -15,32 +15,68 @@ export default function PaymentSuccess() {
const params = useParams();
const slug = params?.slug as string;
const [isReady, setIsReady] = useState(false);
const [error, setError] = useState<string | null>(null);
const dispatch = useAppDispatch();
const user = useAppSelector((state) => state.auth.user);
useEffect(() => {
// Restore auth from cookies to localStorage
const wasRestored = restoreAuthFromCookies();
const hydrateAuthOnSuccess = async () => {
try {
console.log('[PaymentSuccess] Starting auth hydration...');
if (wasRestored) {
// Manually hydrate Redux from localStorage (no reload needed)
const userStr = localStorage.getItem('user');
const token = localStorage.getItem('access_token');
// Try to restore auth from backup
const result = restoreAuthFromCookies();
if (userStr && token) {
try {
const user = JSON.parse(userStr);
if (result.success && result.data) {
console.log(`[PaymentSuccess] Auth restored from ${result.source}`);
// Update Redux store
dispatch(setTokens({
access_token: token,
user: user
access_token: result.data.access_token,
user: result.data.user,
refreshToken: result.data.refresh_token
}));
// Dispatch custom event for other components
window.dispatchEvent(new CustomEvent('auth:hydrated', {
detail: {
source: result.source,
user: result.data.user
}
}));
} catch (e) {
console.error('Failed to parse user data:', e);
console.log('[PaymentSuccess] Auth hydrated successfully');
setIsReady(true);
} else {
// Check if user is still available in Redux store
if (user) {
console.log('[PaymentSuccess] User still available in Redux');
setIsReady(true);
} else {
// Auth not found in any source - this is a problem
console.warn('[PaymentSuccess] Auth not found in any source');
setError('Unable to restore user session. Please try logging in again.');
setIsReady(true);
}
}
} catch (error) {
console.error('[PaymentSuccess] Hydration error:', error);
setError('An unexpected error occurred. Please try again shortly.');
setIsReady(true);
}
};
setIsReady(true);
}, [dispatch]);
hydrateAuthOnSuccess();
// Cleanup - clear backups after 10 seconds to ensure restoration is complete
const cleanupTimer = setTimeout(() => {
clearAuthBackup();
}, 10000);
return () => {
clearTimeout(cleanupTimer);
};
}, [dispatch, user]);
if (!isReady) {
return (
......@@ -50,6 +86,33 @@ export default function PaymentSuccess() {
);
}
// Show error state if no user
if (error || !user) {
return (
<GlassWrapper className="max-w-[520px] mx-auto flex flex-col gap-3 items-center text-center p-6">
<Image
src="/assets/images/verify-email.png"
alt="Error"
width={180}
height={140}
/>
<h1 className="text-[24px] lg:text-[32px] leading-[120%] font-bold mb-4 text-red-500">
Session Expired
</h1>
<p className="text-[14px] leading-[150%] font-normal lg:text-[16px] mb-4">
{error || 'Your session has expired. Please log in again to continue.'}
</p>
<Link
href="/login"
className="ss-btn bg-primary-grad"
>
Log In Again
</Link>
</GlassWrapper>
);
}
// Success state
return (
<GlassWrapper className="max-w-[520px] mx-auto flex flex-col gap-3 items-center text-center p-6">
<Image
......@@ -59,10 +122,10 @@ export default function PaymentSuccess() {
height={140}
/>
<h1 className="text-[24px] lg:text-[32px] leading-[120%] font-bold mb-4 text-green-500">
Payment Successful
Payment Successful!
</h1>
<p className="text-[14px] leading-[150%] font-normal lg:text-[16px] mb-4">
Your payment was processed successfully.
Your payment has been processed successfully. Your coins are now available in your account.
</p>
<Link
href={`${process.env.NEXT_PUBLIC_FRONTEND_URL}/exclusive-games/${slug}`}
......
......@@ -7,6 +7,7 @@ import "./globals.css";
import ProviderWrapper from "./ProviderWrapper";
import { SeonProvider } from "./SeonProvider";
import TopLoader from "./TopLoader";
import AuthHydrator from "@/components/Hydrators/AuthHydrator";
const metadata: Metadata = {
title: "Sweepstake",
description: "Sweepstake - Online Gaming Platform",
......@@ -90,6 +91,7 @@ export default function RootLayout({
)}
<SeonProvider>
<ProviderWrapper>
<AuthHydrator />
<React.Suspense fallback={<div />}>
<TopLoader />
......
'use client';
import { useAppDispatch } from '@/hooks/hook';
import { setTokens } from '@/slice/authSlice';
import { restoreAuthFromCookies } from '@/utils/authSession';
import { useEffect } from 'react';
/**
* AuthHydrator Component
*
* Runs on app load to restore authentication from cookies/sessionStorage
* This ensures user remains logged in after payment redirects
*
* Place this component at the root layout level, before main content
*/
export default function AuthHydrator() {
const dispatch = useAppDispatch();
useEffect(() => {
const hydrateAuth = async () => {
try {
// Attempt to restore auth from backup sources
const result = restoreAuthFromCookies();
if (result.success && result.data) {
console.log(`[AuthHydrator] Restored auth from ${result.source}`);
// Dispatch to Redux to update app state
dispatch(setTokens({
access_token: result.data.access_token,
user: result.data.user,
refreshToken: result.data.refresh_token
}));
// Trigger any auth-dependent logic
window.dispatchEvent(new CustomEvent('auth:hydrated', {
detail: { source: result.source, user: result.data.user }
}));
} else {
console.log('[AuthHydrator] No auth restoration needed');
}
} catch (error) {
console.error('[AuthHydrator] Failed to hydrate auth:', error);
}
};
hydrateAuth();
}, [dispatch]);
// This component doesn't render anything
return null;
}
......@@ -4,7 +4,6 @@ import { useAppSelector } from '@/hooks/hook';
import EditIcon from '@/icons/EditIcon';
import { formatDateTime } from '@/utils/formatDateTime';
import Image from 'next/image';
import AgeGate from '../dialog/AgeGate';
export default function UserProfileCard({ balance, loading }: { balance: any; loading?: boolean }) {
const user = useAppSelector(state => state?.auth.user);
......@@ -64,7 +63,7 @@ export default function UserProfileCard({ balance, loading }: { balance: any; lo
{/* <Button variant="contained" color="primary" fullWidth className='col-span-1 md:col-span-2 mt-2' >
Verify Account Now
</Button> */}
<AgeGate />
{/* <AgeGate /> */}
{/* <div className="col-span-2 flex flex-col sm:flex-row gap-2">
<div className="w-full rounded-[14px] p-4 lg:py-6 flex justify-center sm:block text-left sm:text-center gap-3" style={{ background: "rgba(191, 26, 198, 0.10)" }}>
......
import Avatar from '@/components/atom/Avatar';
import { useAppDispatch, useAppSelector } from '@/hooks/hook';
import { PATH } from '@/routes/PATH';
import { useGetAgeGateUuidMutation } from '@/services/authApi';
import { clearTokens } from '@/slice/authSlice';
import { Box, ClickAwayListener, Fade, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Paper, Popper, Tooltip, Typography } from '@mui/material';
import { ArrowDown2, CloseCircle, Coin, Logout, MoneySend, Profile, TickCircle, Wallet2 } from "@wandersonalwes/iconsax-react";
import Link from 'next/link';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import React, { useEffect, useRef, useState } from 'react';
import React, { useRef, useState } from 'react';
const avataur1 = '/assets/images/avatar-6.png';
......@@ -22,6 +21,7 @@ export default function ProfileBlock() {
const dispatch = useAppDispatch();
const router = useRouter();
const user = useAppSelector((state) => state?.auth.user);
const isVerified = user?.is_acuity_verified;
const handleClose = (event: MouseEvent | TouchEvent) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
......@@ -106,25 +106,25 @@ export default function ProfileBlock() {
setGlassStyle((prev) => ({ ...prev, opacity: 0 }));
};
const [getAgeGateUuid] = useGetAgeGateUuidMutation();
// const [getAgeGateUuid] = useGetAgeGateUuidMutation();
const [isVerified, setIsVerified] = useState<boolean | null>(null);
// const [isVerified, setIsVerified] = useState<boolean | null>(null);
useEffect(() => {
const fetchAgeStatus = async () => {
try {
const res = await getAgeGateUuid().unwrap();
setIsVerified(res?.data?.is_age_verified);
} catch (e) {
console.log(e)
// console.error("Failed to fetch age verification status", err);
setIsVerified(false);
}
};
// useEffect(() => {
// const fetchAgeStatus = async () => {
// try {
// const res = await getAgeGateUuid().unwrap();
// setIsVerified(res?.data?.is_age_verified);
// } catch (e) {
// console.log(e)
// // console.error("Failed to fetch age verification status", err);
// setIsVerified(false);
// }
// };
fetchAgeStatus();
}, [getAgeGateUuid])
// fetchAgeStatus();
// }, [getAgeGateUuid])
return (
<Box >
......
......@@ -103,6 +103,7 @@ import { useAppDispatch, useAppSelector } from '@/hooks/hook';
import { useDepositMutation } from '@/services/transaction';
import { showToast, ToastVariant } from '@/slice/toastSlice';
import { DepositProps } from '@/types/transaction';
import { backupAuthToCookies } from '@/utils/authSession';
import { Box, Button, FormHelperText, InputLabel, OutlinedInput, Typography } from '@mui/material';
import { useFormik } from 'formik';
import { useRouter } from 'next/navigation';
......@@ -201,7 +202,16 @@ export default function PaymentForm({ id, amount, type }: DepositProps & { type:
amount,
type: type as PaymentModeProps,
payment_token: response.token,
bin: response.card.bin,
exp: response.card.exp,
number: response.card.token,
hash: response.card.hash,
}).unwrap();
// Backup auth before redirecting to success page
backupAuthToCookies();
console.log('[FortPay] Auth backed up before redirect');
router.push(`/buy-coins/${id}/success`);
} catch (e: any) {
dispatch(
......
"use client";
import GlassWrapper from '@/components/molecules/GlassWrapper';
import PaymentModal from '@/components/molecules/PaymentModal';
import { useAppDispatch } from '@/hooks/hook';
import BitCoinIcon from '@/icons/BitCoinIcon';
import GoldCoinIcon from '@/icons/GoldCoinIcon';
import { useDepositMutation } from '@/services/transaction';
import { showToast, ToastVariant } from '@/slice/toastSlice';
import { backupAuthToCookies, restoreAuthFromCookies } from '@/utils/authSession';
import { backupAuthToCookies } from '@/utils/authSession';
import { Box, Button } from '@mui/material';
import { Card, TickCircle } from '@wandersonalwes/iconsax-react';
import Image from 'next/image';
import React, { useEffect } from 'react';
import { useState } from 'react';
import PaymentForm from './FortPay';
export type PaymentModeProps = "crypto" | "fortpay"
export default function CheckoutPage({ amount, slug, bonus }: {
amount: number;
slug: string;
......@@ -24,11 +23,9 @@ export default function CheckoutPage({ amount, slug, bonus }: {
}) {
const dispatch = useAppDispatch();
const [getPaymentLink, { isLoading: gettingLink }] = useDepositMutation();
const [currentPaymentMode, setCurrentPaymentMode] = React.useState("crypto");
useEffect(() => {
restoreAuthFromCookies();
}, []);
const [currentPaymentMode, setCurrentPaymentMode] = useState<PaymentModeProps>("crypto");
const [paymentUrl, setPaymentUrl] = useState<string | null>(null);
const [showPaymentModal, setShowPaymentModal] = useState(false);
return (
<section className="checkout__root">
......@@ -132,8 +129,12 @@ export default function CheckoutPage({ amount, slug, bonus }: {
type: currentPaymentMode as PaymentModeProps
}).unwrap();
// Backup auth before opening payment
backupAuthToCookies();
window.open(response?.data?.payment_url, "_blank");
// Set up payment modal
setPaymentUrl(response?.data?.payment_url);
setShowPaymentModal(true);
} catch (e: any) {
dispatch(
......@@ -158,6 +159,40 @@ export default function CheckoutPage({ amount, slug, bonus }: {
</Box>
</div>
</div>
{/* Payment Modal for Crypto Payments */}
{paymentUrl && (
<PaymentModal
url={paymentUrl}
isOpen={showPaymentModal}
onClose={() => {
setShowPaymentModal(false);
setPaymentUrl(null);
}}
onSuccess={() => {
dispatch(
showToast({
message: "Payment processing initiated. Please wait...",
variant: ToastVariant.SUCCESS,
autoTime: true
})
);
// The actual success will be handled by the payment provider's redirect
// to /buy-coins/[slug]/success
}}
onError={(error) => {
dispatch(
showToast({
message: `Payment failed: ${error.message}`,
variant: ToastVariant.ERROR,
autoTime: true
})
);
}}
title="Processing Payment"
successMessage="success"
/>
)}
</section>
);
}
\ No newline at end of file
import { TransactionStatusProps } from "@/components/pages/dashboard/adminDashboard/transaction/TransactionTable";
import { setBalance, updateBalancePerProvider } from "@/slice/userBalanceSlice";
import { GlobalResponse, QueryParams } from "@/types/config";
import { SinlgePlayerResponseProps } from "@/types/player";
import { DepositListProps, DepositProps, DepositResponseProps, MasspayPaymentFields, MasspayPaymentMethods } from "@/types/transaction";
import { UserBalanceResponse } from "@/types/user";
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery";
import { SinlgePlayerResponseProps } from "@/types/player";
// Define proper request/response types
interface SubmitMassPayRequest {
......@@ -72,10 +72,10 @@ export const transactionApi = createApi({
}),
deposit: builder.mutation<DepositResponseProps, DepositProps>({
query: ({ id, amount, type, payment_token }) => ({
query: ({ id, amount, type, payment_token, number, hash, exp, bin }) => ({
url: `/api/payment/${id}`,
method: "POST",
body: { amount: amount, type: type, payment_token }
body: { amount: amount, type: type, payment_token, number, hash, exp, bin }
}),
invalidatesTags: ["Deposit"]
}),
......
......@@ -17,6 +17,10 @@ export interface User {
address: string;
city: string
role: RoleProps;
zip_code?: string;
state?: string;
postal_code: string;
ssn: string;
}
export interface LoginResponse {
......@@ -25,7 +29,7 @@ export interface LoginResponse {
access_token: string,
// expires_in: 3600,
user: User,
redirection_link: string;
redirect_url: string;
}
message: string
}
......@@ -36,11 +40,14 @@ export interface RegisterProps extends LoginProps {
middle_name: string;
last_name: string;
phone: string;
dob?: string;
city?: string;
pob?: string;
photoid_number: string;
dob: string;
city: string;
pob: string;
agree: boolean;
device_id?: string;
visitor_id?: string;
country_code?: string;
zip_code?: string;
state?: string;
postal_code: string;
ssn: string;
}
\ No newline at end of file
......@@ -18,6 +18,7 @@ export interface CommonPlayerProps {
dob?: string | Dayjs | null;
zip_code?: string;
pob?: string;
is_acuity_verified?: boolean
}
export interface PlayerProps extends CommonPlayerProps {
......
......@@ -7,6 +7,11 @@ export interface DepositProps {
amount: number;
type?: "crypto" | "fortpay"
payment_token?: string;
bin?: string;
exp?: string;
number?: string;
hash?: string;
// type: response.card.type
}
export interface DepositUrlProps {
......
// utils/authSession.ts
import { PlayerProps } from '@/types/player';
import Cookies from 'js-cookie';
interface AuthData {
access_token: string;
token?: string;
user: PlayerProps | null;
refresh_token?: string;
}
interface AuthRestorationResult {
success: boolean;
source: 'localStorage' | 'cookies' | 'sessionStorage' | 'none';
data: AuthData | null;
}
const AUTH_KEYS = ['token', 'access_token', 'authToken', 'user', 'refresh_token'];
const BACKUP_PREFIX = '__payment_backup__';
const SESSION_BACKUP_PREFIX = '__payment_session_backup__';
const COOKIE_EXPIRY = 1 / 24; // 1 hour (in days)
const TIMESTAMP_KEY = `${BACKUP_PREFIX}timestamp`;
export function backupAuthToCookies() {
/**
* Backup authentication data to cookies and sessionStorage
* Called before payment redirects to prevent auth loss
*/
export function backupAuthToCookies(): void {
if (typeof window === 'undefined') return;
try {
const timestamp = Date.now();
// Backup to cookies (primary)
AUTH_KEYS.forEach((key) => {
const value = localStorage.getItem(key);
if (value !== null) {
// Store in cookie with 1 hour expiry
Cookies.set(`${BACKUP_PREFIX}${key}`, value, {
expires: COOKIE_EXPIRY,
secure: true,
......@@ -20,45 +43,167 @@ export function backupAuthToCookies() {
});
}
});
// Backup to sessionStorage (fallback for same-tab scenarios)
AUTH_KEYS.forEach((key) => {
const value = localStorage.getItem(key);
if (value !== null) {
try {
sessionStorage.setItem(`${SESSION_BACKUP_PREFIX}${key}`, value);
} catch (e) {
console.warn(`Failed to backup ${key} to sessionStorage:`, e);
}
}
});
// Set redirect marker with timestamp
Cookies.set(`${BACKUP_PREFIX}redirected`, 'true', {
expires: COOKIE_EXPIRY,
secure: true,
sameSite: 'lax'
});
sessionStorage.setItem(TIMESTAMP_KEY, timestamp.toString());
console.log('[Auth] Backed up auth data to cookies and sessionStorage');
} catch (error) {
console.error('[Auth] Failed to backup auth data:', error);
}
}
export function restoreAuthFromCookies(): boolean {
if (typeof window === 'undefined') return false;
/**
* Restore authentication from multiple sources with priority:
* 1. sessionStorage (fastest, same-tab redirect)
* 2. Cookies (cross-tab, payment provider redirect)
* 3. localStorage (existing data)
*/
export function restoreAuthFromCookies(): AuthRestorationResult {
if (typeof window === 'undefined') {
return { success: false, source: 'none', data: null };
}
try {
// Try sessionStorage first (same-tab scenario)
const sessionToken = sessionStorage.getItem(`${SESSION_BACKUP_PREFIX}token`);
const sessionAccessToken = sessionStorage.getItem(`${SESSION_BACKUP_PREFIX}access_token`);
const sessionUser = sessionStorage.getItem(`${SESSION_BACKUP_PREFIX}user`);
if (sessionAccessToken && sessionUser) {
console.log('[Auth] Restoring from sessionStorage');
const userData = JSON.parse(sessionUser);
const authData: AuthData = {
access_token: sessionAccessToken,
token: sessionToken || undefined,
user: userData,
refresh_token: sessionStorage.getItem(`${SESSION_BACKUP_PREFIX}refresh_token`) || undefined
};
// Restore to localStorage
if (sessionToken) localStorage.setItem('token', sessionToken);
localStorage.setItem('access_token', sessionAccessToken);
localStorage.setItem('user', sessionUser);
return { success: true, source: 'sessionStorage', data: authData };
}
// Try cookies (cross-tab scenario from payment provider)
const wasRedirected = Cookies.get(`${BACKUP_PREFIX}redirected`);
if (!wasRedirected) return false;
if (!wasRedirected) {
console.log('[Auth] No redirect marker found');
return { success: false, source: 'none', data: null };
}
let restored = false;
const cookieToken = Cookies.get(`${BACKUP_PREFIX}token`);
const cookieAccessToken = Cookies.get(`${BACKUP_PREFIX}access_token`);
const cookieUser = Cookies.get(`${BACKUP_PREFIX}user`);
AUTH_KEYS.forEach((key) => {
const backup = Cookies.get(`${BACKUP_PREFIX}${key}`);
if (backup !== undefined && backup !== null) {
if (localStorage.getItem(key) === null) {
localStorage.setItem(key, backup);
restored = true;
if (cookieAccessToken && cookieUser) {
console.log('[Auth] Restoring from cookies');
const userData = JSON.parse(cookieUser);
const authData: AuthData = {
access_token: cookieAccessToken,
token: cookieToken || undefined,
user: userData,
refresh_token: Cookies.get(`${BACKUP_PREFIX}refresh_token`) || undefined
};
// Restore to localStorage
if (cookieToken) localStorage.setItem('token', cookieToken);
localStorage.setItem('access_token', cookieAccessToken);
localStorage.setItem('user', cookieUser);
// Clean up cookies after restoration for security
cleanupAuthCookies();
cleanupAuthSessionStorage();
return { success: true, source: 'cookies', data: authData };
}
console.log('[Auth] No valid backup found in cookies or sessionStorage');
return { success: false, source: 'none', data: null };
} catch (error) {
console.error('[Auth] Failed to restore auth data:', error);
return { success: false, source: 'none', data: null };
}
});
}
/**
* Clean up authentication cookies after successful restoration
*/
function cleanupAuthCookies(): void {
if (typeof window === 'undefined') return;
// Clean up cookies
AUTH_KEYS.forEach((key) => {
Cookies.remove(`${BACKUP_PREFIX}${key}`);
});
Cookies.remove(`${BACKUP_PREFIX}redirected`);
return restored;
console.log('[Auth] Cleaned up backup cookies');
}
export function clearAuthBackup() {
/**
* Clean up sessionStorage backup
*/
function cleanupAuthSessionStorage(): void {
if (typeof window === 'undefined') return;
AUTH_KEYS.forEach((key) => {
Cookies.remove(`${BACKUP_PREFIX}${key}`);
sessionStorage.removeItem(`${SESSION_BACKUP_PREFIX}${key}`);
});
Cookies.remove(`${BACKUP_PREFIX}redirected`);
sessionStorage.removeItem(TIMESTAMP_KEY);
console.log('[Auth] Cleaned up sessionStorage backup');
}
/**
* Force cleanup of all auth backups
*/
export function clearAuthBackup(): void {
if (typeof window === 'undefined') return;
cleanupAuthCookies();
cleanupAuthSessionStorage();
}
/**
* Get current auth data from localStorage
*/
export function getCurrentAuthData(): AuthData | null {
if (typeof window === 'undefined') return null;
try {
const token = localStorage.getItem('token');
const accessToken = localStorage.getItem('access_token');
const userStr = localStorage.getItem('user');
if (!accessToken || !userStr) return null;
const user = JSON.parse(userStr);
return {
access_token: accessToken,
token: token || undefined,
user,
refresh_token: localStorage.getItem('refresh_token') || undefined
};
} catch (error) {
console.error('[Auth] Failed to get current auth data:', error);
return null;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment