"use client"; import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { consumeSubscriptionExitUrl, peekSubscriptionExplicitExitUrl, } from "@/lib/navigation/subscription_exit"; import { clearPendingPaymentOrder, getPendingPaymentOrderForType, } from "@/lib/payment/pending_payment_order"; import { usePaymentDispatch, usePaymentState, } from "@/stores/payment/payment-context"; import { useUserDispatch } from "@/stores/user/user-context"; import type { PayChannel } from "@/data/dto/payment"; import type { SubscriptionType } from "./subscription-screen.helpers"; export interface UseSubscriptionPaymentFlowInput { subscriptionType: SubscriptionType; shouldResumePendingOrder: boolean; returnTo: "chat" | null; initialPayChannel: PayChannel; } export function useSubscriptionPaymentFlow({ subscriptionType, shouldResumePendingOrder, returnTo, initialPayChannel, }: UseSubscriptionPaymentFlowInput) { const router = useRouter(); const userDispatch = useUserDispatch(); const payment = usePaymentState(); const paymentDispatch = usePaymentDispatch(); const refreshedPaidOrderRef = useRef(null); const resumedPendingOrderRef = useRef(null); const successDialogShownOrderRef = useRef(null); const initialPayChannelAppliedRef = useRef(false); const [showPaymentSuccessDialog, setShowPaymentSuccessDialog] = useState(false); useEffect(() => { if (payment.status === "idle") { initialPayChannelAppliedRef.current = true; paymentDispatch({ type: "PaymentInit", payChannel: initialPayChannel, }); return; } if ( !initialPayChannelAppliedRef.current && payment.status === "ready" ) { initialPayChannelAppliedRef.current = true; if (payment.payChannel === initialPayChannel) return; paymentDispatch({ type: "PaymentPayChannelChanged", payChannel: initialPayChannel, }); } }, [ initialPayChannel, payment.payChannel, payment.status, paymentDispatch, ]); useEffect(() => { if (!payment.isPaid || !payment.currentOrderId) return; if (refreshedPaidOrderRef.current === payment.currentOrderId) return; refreshedPaidOrderRef.current = payment.currentOrderId; userDispatch({ type: "UserFetch" }); }, [payment.currentOrderId, payment.isPaid, userDispatch]); useEffect(() => { if (!payment.isPaid || !payment.currentOrderId) return; if (successDialogShownOrderRef.current === payment.currentOrderId) return; successDialogShownOrderRef.current = payment.currentOrderId; setShowPaymentSuccessDialog(true); }, [payment.currentOrderId, payment.isPaid]); useEffect(() => { const canInspectPendingOrder = payment.status === "ready" || (!shouldResumePendingOrder && (payment.isPollingOrder || payment.isPaid || payment.status === "failed")); if (!canInspectPendingOrder) return; let cancelled = false; const handlePendingOrder = async () => { const result = await getPendingPaymentOrderForType(subscriptionType); if (cancelled || !result.success || result.data === null) return; if (!shouldResumePendingOrder) { await clearPendingPaymentOrder(); if ( payment.currentOrderId === result.data.orderId && (payment.isPollingOrder || payment.isPaid || payment.status === "failed") ) { paymentDispatch({ type: "PaymentReset" }); } return; } if (payment.currentOrderId === result.data.orderId) return; if (resumedPendingOrderRef.current === result.data.orderId) return; resumedPendingOrderRef.current = result.data.orderId; paymentDispatch({ type: "PaymentReturned", orderId: result.data.orderId, createdAt: result.data.createdAt, }); }; void handlePendingOrder(); return () => { cancelled = true; }; }, [ payment.currentOrderId, payment.isPaid, payment.isPollingOrder, payment.status, paymentDispatch, shouldResumePendingOrder, subscriptionType, ]); useEffect(() => { if (!payment.currentOrderId) return; if (!payment.isPaid && payment.status !== "failed") return; void clearPendingPaymentOrder(); }, [payment.currentOrderId, payment.isPaid, payment.status]); const handleBackClick = () => { void (async () => { router.replace(await consumeSubscriptionExitUrl(returnTo)); })(); }; const handlePaymentSuccessClose = () => { void (async () => { setShowPaymentSuccessDialog(false); paymentDispatch({ type: "PaymentReset" }); const pendingExitUrl = await peekSubscriptionExplicitExitUrl(); if (pendingExitUrl) { router.replace(pendingExitUrl); return; } router.replace(await consumeSubscriptionExitUrl(returnTo)); })(); }; return { payment, paymentDispatch, showPaymentSuccessDialog, handleBackClick, handlePaymentSuccessClose, }; }