feat(payment): expand Stripe methods and checkout handoff
Docker Image / Build and Push Docker Image (push) Successful in 2m10s
Docker Image / Build and Push Docker Image (push) Successful in 2m10s
This commit is contained in:
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
|
||||
|
||||
import {
|
||||
persistExternalEntryPayload,
|
||||
resolveCheckoutIntentDestination,
|
||||
resolveExternalEntryDestination,
|
||||
resolveExternalEntryPromotionType,
|
||||
resolveExternalEntryTarget,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
import { Logger } from "@/utils/logger";
|
||||
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
|
||||
import { consumeTopUpHandoff } from "@/lib/auth/top_up_handoff";
|
||||
import { consumeCheckoutHandoff } from "@/lib/auth/checkout_handoff";
|
||||
import { Result } from "@/utils/result";
|
||||
import {
|
||||
isFavoriteEntryRequest,
|
||||
@@ -61,6 +63,9 @@ export default function ExternalEntryPersist({
|
||||
const [hasPersisted, setHasPersisted] = useState(false);
|
||||
const [handoffError, setHandoffError] = useState<string | null>(null);
|
||||
const [handoffCompleted, setHandoffCompleted] = useState(false);
|
||||
const [checkoutDestination, setCheckoutDestination] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const hasNavigatedRef = useRef(false);
|
||||
const handoffStartedRef = useRef(false);
|
||||
const targetRoute = resolveExternalEntryTarget({ target });
|
||||
@@ -71,11 +76,14 @@ export default function ExternalEntryPersist({
|
||||
mode,
|
||||
promotionType,
|
||||
});
|
||||
const isTopUpHandoff = targetRoute === ROUTES.subscription;
|
||||
const normalizedTarget = target?.trim().toLowerCase() ?? "";
|
||||
const isTopUpHandoff = normalizedTarget === "topup";
|
||||
const isCheckoutHandoff = normalizedTarget === "checkout";
|
||||
const isLoginHandoff = isTopUpHandoff || isCheckoutHandoff;
|
||||
const displayedHandoffError =
|
||||
handoffError ??
|
||||
(isTopUpHandoff && !hasValue(handoffToken)
|
||||
? "This top-up link is invalid. Please request a new link in Messenger."
|
||||
(isLoginHandoff && !hasValue(handoffToken)
|
||||
? "This checkout link is invalid. Please return and request a new link."
|
||||
: null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -128,7 +136,7 @@ export default function ExternalEntryPersist({
|
||||
useEffect(() => {
|
||||
if (hasNavigatedRef.current || !hasPersisted) return;
|
||||
|
||||
if (isTopUpHandoff) {
|
||||
if (isLoginHandoff) {
|
||||
if (!authState.hasInitialized || authState.isLoading) return;
|
||||
if (handoffCompleted) {
|
||||
if (
|
||||
@@ -138,25 +146,60 @@ export default function ExternalEntryPersist({
|
||||
return;
|
||||
}
|
||||
hasNavigatedRef.current = true;
|
||||
router.replace(destination);
|
||||
router.replace(checkoutDestination ?? destination);
|
||||
return;
|
||||
}
|
||||
if (handoffStartedRef.current) return;
|
||||
if (!hasValue(handoffToken)) return;
|
||||
handoffStartedRef.current = true;
|
||||
void (async () => {
|
||||
const result = await consumeTopUpHandoff(handoffToken);
|
||||
if (Result.isErr(result)) {
|
||||
log.warn("[ExternalEntryPersist] top-up handoff failed", result.error);
|
||||
if (isCheckoutHandoff) {
|
||||
const result = await consumeCheckoutHandoff(handoffToken);
|
||||
if (Result.isErr(result)) {
|
||||
log.warn(
|
||||
"[ExternalEntryPersist] checkout handoff failed",
|
||||
result.error,
|
||||
);
|
||||
window.history.replaceState(
|
||||
window.history.state,
|
||||
"",
|
||||
"/external-entry?target=checkout",
|
||||
);
|
||||
setHandoffError(
|
||||
"This checkout link is invalid, expired, or already used. Please return and request a new link.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
window.history.replaceState(
|
||||
window.history.state,
|
||||
"",
|
||||
"/external-entry?target=checkout",
|
||||
);
|
||||
setCheckoutDestination(
|
||||
resolveCheckoutIntentDestination(result.data),
|
||||
);
|
||||
} else {
|
||||
const result = await consumeTopUpHandoff(handoffToken);
|
||||
if (Result.isErr(result)) {
|
||||
log.warn(
|
||||
"[ExternalEntryPersist] top-up handoff failed",
|
||||
result.error,
|
||||
);
|
||||
window.history.replaceState(
|
||||
window.history.state,
|
||||
"",
|
||||
"/external-entry?target=topup",
|
||||
);
|
||||
setHandoffError(
|
||||
"This top-up link is invalid or has expired. Please request a new link in Messenger.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
window.history.replaceState(
|
||||
window.history.state,
|
||||
"",
|
||||
"/external-entry?target=topup",
|
||||
);
|
||||
setHandoffError(
|
||||
"This top-up link is invalid or has expired. Please request a new link in Messenger.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
setHandoffCompleted(true);
|
||||
authDispatch({ type: "AuthInit" });
|
||||
@@ -190,9 +233,12 @@ export default function ExternalEntryPersist({
|
||||
authState.isLoading,
|
||||
authState.loginStatus,
|
||||
destination,
|
||||
checkoutDestination,
|
||||
hasPersisted,
|
||||
handoffCompleted,
|
||||
handoffToken,
|
||||
isCheckoutHandoff,
|
||||
isLoginHandoff,
|
||||
isTopUpHandoff,
|
||||
psid,
|
||||
router,
|
||||
|
||||
Reference in New Issue
Block a user