From a0dc4f449e0422c85299baf0a854e76370ed823b Mon Sep 17 00:00:00 2001 From: Aman Raj <113578582+huamanraj@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:13:47 +0530 Subject: [PATCH 1/4] fix: pro user instant verification --- apps/web/src/components/payment/PaymentFlow.tsx | 14 +++++++++++++- apps/web/src/hooks/useSubscription.ts | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/payment/PaymentFlow.tsx b/apps/web/src/components/payment/PaymentFlow.tsx index fe1f2e0f..b626babb 100644 --- a/apps/web/src/components/payment/PaymentFlow.tsx +++ b/apps/web/src/components/payment/PaymentFlow.tsx @@ -50,6 +50,7 @@ const PaymentFlow: React.FC = ({ amount: number; // Stored for display purposes only } | null>(null); + const utils = trpc.useUtils(); const createOrderMutation = (trpc.payment as any).createOrder.useMutation(); const verifyPaymentMutation = ( trpc.payment as any @@ -71,7 +72,18 @@ const PaymentFlow: React.FC = ({ planId: planId, }); - // Show success and redirect + // invalidate cache and fetch fresh subscription status before redirect + // this ensures the checkout page shows the updated status immediately + // wrap in Promise.race with timeout to prevent hanging + await (utils.user as any).subscriptionStatus.invalidate(); + await Promise.race([ + (utils.user as any).subscriptionStatus.fetch(undefined, { + throwOnError: false, + }), + new Promise((resolve) => setTimeout(resolve, 3000)), // 3s timeout + ]); + + // redirect after subscription status is fetched and cached router.push("/checkout"); } catch (error) { console.error("Verification failed:", error); diff --git a/apps/web/src/hooks/useSubscription.ts b/apps/web/src/hooks/useSubscription.ts index 943c3794..83925371 100644 --- a/apps/web/src/hooks/useSubscription.ts +++ b/apps/web/src/hooks/useSubscription.ts @@ -19,18 +19,21 @@ export function useSubscription() { isLoading, } = useSubscriptionStore(); + const utils = trpc.useUtils(); + // Fetch subscription status using tRPC const { data, isLoading: isFetching, isError, isFetched, + refetch, } = (trpc.user as any).subscriptionStatus.useQuery(undefined, { enabled: !!session?.user && status === "authenticated", - refetchOnWindowFocus: false, + refetchOnWindowFocus: true, // refetch when user returns to tab refetchOnMount: true, - staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes - gcTime: 10 * 60 * 1000, // Keep in cache for 10 minutes + staleTime: 2 * 60 * 1000, // consider data fresh for 2 minutes (reduced from 5) + gcTime: 10 * 60 * 1000, // keep in cache for 10 minutes }); useEffect(() => { @@ -69,9 +72,16 @@ export function useSubscription() { reset, ]); + // manual refetch function for immediate cache invalidation + const refetchSubscription = async () => { + await (utils.user as any).subscriptionStatus.invalidate(); + await refetch(); + }; + return { isPaidUser, subscription, isLoading, + refetchSubscription, // expose manual refetch function }; } From 685638b78192afcd83024342cad48e94785fa5a4 Mon Sep 17 00:00:00 2001 From: Aman Raj <113578582+huamanraj@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:28:59 +0530 Subject: [PATCH 2/4] refactor: improve payment flow error handling and subscription cache refresh --- .../src/components/payment/PaymentFlow.tsx | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/apps/web/src/components/payment/PaymentFlow.tsx b/apps/web/src/components/payment/PaymentFlow.tsx index b626babb..bc7e8a2e 100644 --- a/apps/web/src/components/payment/PaymentFlow.tsx +++ b/apps/web/src/components/payment/PaymentFlow.tsx @@ -72,18 +72,27 @@ const PaymentFlow: React.FC = ({ planId: planId, }); - // invalidate cache and fetch fresh subscription status before redirect - // this ensures the checkout page shows the updated status immediately - // wrap in Promise.race with timeout to prevent hanging - await (utils.user as any).subscriptionStatus.invalidate(); - await Promise.race([ - (utils.user as any).subscriptionStatus.fetch(undefined, { - throwOnError: false, - }), - new Promise((resolve) => setTimeout(resolve, 3000)), // 3s timeout - ]); - - // redirect after subscription status is fetched and cached + // payment verification succeeded - proceed with redirect + // subscription cache refresh is decoupled as best-effort background action + // errors in refresh won't affect the successful payment verification + (async () => { + try { + await (utils.user as any).subscriptionStatus.invalidate(); + await Promise.race([ + (utils.user as any).subscriptionStatus.fetch(undefined), + new Promise((resolve) => setTimeout(resolve, 3000)), // 3s timeout + ]); + } catch (refreshError) { + // log refresh errors separately without affecting payment flow + console.warn( + "subscription cache refresh failed (non-fatal):", + refreshError + ); + } + })(); + + // redirect immediately after successful verification + // checkout page will refetch subscription status if cache refresh failed router.push("/checkout"); } catch (error) { console.error("Verification failed:", error); From 8ac6eb81c4e32c3842c2ca99c4a566cf90e37941 Mon Sep 17 00:00:00 2001 From: Aman Raj <113578582+huamanraj@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:34:41 +0530 Subject: [PATCH 3/4] fix: Replaced the hardcoded router.push --- apps/web/src/components/payment/PaymentFlow.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/payment/PaymentFlow.tsx b/apps/web/src/components/payment/PaymentFlow.tsx index bc7e8a2e..ff698856 100644 --- a/apps/web/src/components/payment/PaymentFlow.tsx +++ b/apps/web/src/components/payment/PaymentFlow.tsx @@ -6,7 +6,7 @@ import { useRazorpay } from "@/hooks/useRazorpay"; import type { RazorpayOptions } from "@/lib/razorpay"; import PrimaryButton from "@/components/ui/custom-button"; import { useSession } from "next-auth/react"; -import { useRouter } from "next/navigation"; +import { useRouter, usePathname } from "next/navigation"; interface PaymentFlowProps { planId: string; // Required: Plan ID from database @@ -44,6 +44,7 @@ const PaymentFlow: React.FC = ({ }) => { const { data: session, status: sessionStatus } = useSession(); const router = useRouter(); + const pathname = usePathname(); const [isProcessing, setIsProcessing] = useState(false); const orderDataRef = useRef<{ orderId: string; @@ -122,7 +123,7 @@ const PaymentFlow: React.FC = ({ } if (sessionStatus === "unauthenticated" || !session) { - router.push("/login?callbackUrl=/pricing"); + router.push(`/login?callbackUrl=${encodeURIComponent(pathname)}`); return; } @@ -173,7 +174,7 @@ const PaymentFlow: React.FC = ({ } catch (error: any) { console.warn("Failed to create order:", error); setIsProcessing(false); - router.push("/login?callbackUrl=/pricing"); + router.push(`/login?callbackUrl=${encodeURIComponent(pathname)}`); } }; From f1d82cfa580ed6d56d281e6aff0ffaf83dad6152 Mon Sep 17 00:00:00 2001 From: Aman Raj <113578582+huamanraj@users.noreply.github.com> Date: Sun, 30 Nov 2025 01:44:47 +0530 Subject: [PATCH 4/4] fix: invest button improved error handling --- .../src/components/payment/PaymentFlow.tsx | 27 +++++++++++++++++-- apps/web/src/lib/trpc-server.ts | 4 +-- apps/web/src/providers/trpc-provider.tsx | 2 +- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/payment/PaymentFlow.tsx b/apps/web/src/components/payment/PaymentFlow.tsx index ff698856..0a5185c8 100644 --- a/apps/web/src/components/payment/PaymentFlow.tsx +++ b/apps/web/src/components/payment/PaymentFlow.tsx @@ -127,6 +127,12 @@ const PaymentFlow: React.FC = ({ return; } + // check if session has accessToken - if not, re-authenticate + if (!session.accessToken) { + router.push(`/login?callbackUrl=${encodeURIComponent(pathname)}`); + return; + } + setIsProcessing(true); const razorpayKey = process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID; @@ -172,9 +178,26 @@ const PaymentFlow: React.FC = ({ await initiatePayment(options); } catch (error: any) { - console.warn("Failed to create order:", error); + console.error("Failed to create order:", error); setIsProcessing(false); - router.push(`/login?callbackUrl=${encodeURIComponent(pathname)}`); + + // only redirect to login for authentication errors + const errorMsg = error?.message?.toLowerCase() || ""; + const isAuthError = + error?.data?.code === "UNAUTHORIZED" || + errorMsg.includes("unauthorized") || + errorMsg.includes("not authenticated") || + errorMsg.includes("authentication failed") || + errorMsg.includes("missing or invalid authorization"); + + if (isAuthError) { + router.push(`/login?callbackUrl=${encodeURIComponent(pathname)}`); + } else { + // show error message for non-auth errors + const errorMessage = + error?.message || "Failed to process payment. Please try again."; + alert(errorMessage); + } } }; diff --git a/apps/web/src/lib/trpc-server.ts b/apps/web/src/lib/trpc-server.ts index fb8cebbd..3e757946 100644 --- a/apps/web/src/lib/trpc-server.ts +++ b/apps/web/src/lib/trpc-server.ts @@ -10,7 +10,7 @@ export const serverTrpc = createTRPCProxyClient({ links: [ httpBatchLink({ transformer: superjson, - url: `${process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000"}/trpc`, + url: `${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080"}/trpc`, headers() { return {}; }, @@ -26,7 +26,7 @@ export function createAuthenticatedClient(session: Session) { links: [ httpBatchLink({ transformer: superjson, - url: `${process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000"}/trpc`, + url: `${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080"}/trpc`, headers() { const token = session.accessToken; if (token) { diff --git a/apps/web/src/providers/trpc-provider.tsx b/apps/web/src/providers/trpc-provider.tsx index 356c9f96..dec193c8 100644 --- a/apps/web/src/providers/trpc-provider.tsx +++ b/apps/web/src/providers/trpc-provider.tsx @@ -23,7 +23,7 @@ export function TRPCProvider({ children }: { children: React.ReactNode }) { // Recreate client when session changes to ensure we get the latest token const trpcClient = useMemo(() => { - const baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000"; + const baseUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080"; const trpcUrl = baseUrl.endsWith("/trpc") ? baseUrl : `${baseUrl}/trpc`; return trpc.createClient({