Merged in fix/STAY-67-redirect-to-webview-after-gla (pull request #2795)

fix(STAY-67): redirect to webview after guarantee on my stay

* fix(STAY-67): redirect to webview after guarantee on my stay

* fix(STAY-67): add callback page for guarantee on webview


Approved-by: Linus Flood
This commit is contained in:
Bianca Widstam
2025-09-15 07:18:58 +00:00
parent 98816b27ce
commit 800948bb94
6 changed files with 173 additions and 81 deletions

View File

@@ -1,15 +1,11 @@
import { notFound } from "next/navigation"
import { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
import { logger } from "@scandic-hotels/common/logger"
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { serverClient } from "@/lib/trpc/server"
import GuaranteeCallbackPage from "@/components/GuaranteeCallback"
import GuaranteeCallback from "@/components/HotelReservation/MyStay/Ancillaries/GuaranteeCallback"
import TrackGuarantee from "@/components/HotelReservation/MyStay/TrackGuarantee"
import type { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import type { LangParams, PageArgs } from "@/types/params"
@@ -34,77 +30,16 @@ export default async function GuaranteePaymentCallbackPage(
if (!status || !confirmationNumber || !refId) {
notFound()
}
const isAncillaryFlow = searchParams.ancillary
const myStayUrl = `${myStay[lang]}?RefId=${encodeURIComponent(refId)}`
const searchObject = new URLSearchParams()
if (status === PaymentCallbackStatusEnum.Success && confirmationNumber) {
if (isAncillaryFlow) {
return (
<GuaranteeCallback
returnUrl={myStayUrl}
refId={refId}
confirmationNumber={confirmationNumber}
lang={lang}
/>
)
}
logger.debug(`[gla-payment-callback] redirecting to: ${myStayUrl}`)
return <TrackGuarantee status={status} redirectUrl={myStayUrl} />
}
let errorMessage = undefined
if (refId) {
try {
const caller = await serverClient()
const bookingStatus = await caller.booking.status({
refId,
})
const { booking } = bookingStatus
const error = booking.errors.find((e) => e.errorCode)
errorMessage =
error?.description ??
`No error message found for booking ${confirmationNumber}, status: ${status}`
searchObject.set(
"errorCode",
error
? error.errorCode.toString()
: BookingErrorCodeEnum.TransactionFailed
)
} catch {
logger.error(
`[gla-payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
)
if (status === PaymentCallbackStatusEnum.Cancel) {
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionCancelled)
} else if (status === PaymentCallbackStatusEnum.Error) {
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
}
}
if (errorMessage) {
logger.error(errorMessage)
}
if (isAncillaryFlow) {
searchObject.set("ancillary", "ancillary")
}
return (
<TrackGuarantee
status={status}
isAncillaryFlow={!!isAncillaryFlow}
redirectUrl={`${myStayUrl}&${searchObject.toString()}`}
errorMessage={errorMessage}
/>
)
}
return <LoadingSpinner />
return (
<GuaranteeCallbackPage
status={status}
confirmationNumber={confirmationNumber}
refId={refId}
myStayUrl={myStayUrl}
lang={lang}
isAncillaryFlow={!!searchParams.ancillary}
/>
)
}

View File

@@ -0,0 +1,46 @@
import { notFound } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import { myStay } from "@/constants/routes/webviews"
import GuaranteeCallbackPage from "@/components/GuaranteeCallback"
import type { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import type { LangParams, PageArgs } from "@/types/params"
export default async function GuaranteePaymentWebViewCallbackPage(
props: PageArgs<
LangParams,
{
status?: PaymentCallbackStatusEnum
RefId?: string
confirmationNumber?: string
ancillary?: string
}
>
) {
const searchParams = await props.searchParams
const params = await props.params
logger.debug(`[gla-payment-callback] callback started`)
const lang = params.lang
const status = searchParams.status
const confirmationNumber = searchParams.confirmationNumber
const refId = searchParams.RefId
if (!status || !confirmationNumber || !refId) {
notFound()
}
const myStayUrl = `${myStay[lang]}?RefId=${encodeURIComponent(refId)}`
return (
<GuaranteeCallbackPage
status={status}
confirmationNumber={confirmationNumber}
refId={refId}
myStayUrl={myStayUrl}
lang={lang}
isAncillaryFlow={!!searchParams.ancillary}
/>
)
}

View File

@@ -0,0 +1,100 @@
import { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import { logger } from "@scandic-hotels/common/logger"
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { serverClient } from "@/lib/trpc/server"
import GuaranteeCallback from "@/components/HotelReservation/MyStay/Ancillaries/GuaranteeCallback"
import TrackGuarantee from "@/components/HotelReservation/MyStay/TrackGuarantee"
import type { Lang } from "@scandic-hotels/common/constants/language"
type GuaranteeMyStayCallbackProps = {
lang: Lang
myStayUrl: string
refId: string
status: PaymentCallbackStatusEnum
confirmationNumber?: string
isAncillaryFlow?: boolean
}
export default async function GuaranteeCallbackPage({
lang,
myStayUrl,
refId,
status,
confirmationNumber,
isAncillaryFlow,
}: GuaranteeMyStayCallbackProps) {
const searchObject = new URLSearchParams()
if (status === PaymentCallbackStatusEnum.Success && confirmationNumber) {
if (isAncillaryFlow) {
return (
<GuaranteeCallback
returnUrl={myStayUrl}
refId={refId}
confirmationNumber={confirmationNumber}
lang={lang}
/>
)
}
logger.debug(`[gla-payment-callback] redirecting to: ${myStayUrl}`)
return <TrackGuarantee status={status} redirectUrl={myStayUrl} />
}
let errorMessage = undefined
if (refId) {
try {
const caller = await serverClient()
const bookingStatus = await caller.booking.status({
refId,
})
const { booking } = bookingStatus
const error = booking.errors.find((e) => e.errorCode)
errorMessage =
error?.description ??
`No error message found for booking ${confirmationNumber}, status: ${status}`
searchObject.set(
"errorCode",
error
? error.errorCode.toString()
: BookingErrorCodeEnum.TransactionFailed
)
} catch {
logger.error(
`[gla-payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
)
if (status === PaymentCallbackStatusEnum.Cancel) {
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionCancelled)
} else if (status === PaymentCallbackStatusEnum.Error) {
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
}
}
if (errorMessage) {
logger.error(errorMessage)
}
if (isAncillaryFlow) {
searchObject.set("ancillary", "ancillary")
}
return (
<TrackGuarantee
status={status}
isAncillaryFlow={!!isAncillaryFlow}
redirectUrl={`${myStayUrl}&${searchObject.toString()}`}
errorMessage={errorMessage}
/>
)
}
return <LoadingSpinner />
}

View File

@@ -18,6 +18,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client"
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
import { isWebview } from "@/constants/routes/webviews"
import { env } from "@/env/client"
import {
AncillaryStepEnum,
@@ -80,7 +81,7 @@ export default function AddAncillaryFlowModal({
const pathname = usePathname()
const [isPriceDetailsOpen, setIsPriceDetailsOpen] = useState(false)
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang)}`
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang, isWebview(pathname))}`
const deliveryTimeOptions = generateDeliveryOptions()
const defaultDeliveryTime = deliveryTimeOptions[0].value

View File

@@ -1,5 +1,6 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { usePathname } from "next/navigation"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
@@ -19,6 +20,7 @@ import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
import { toast } from "@scandic-hotels/design-system/Toast"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { isWebview } from "@/constants/routes/webviews"
import { env } from "@/env/client"
import { useMyStayStore } from "@/stores/my-stay"
@@ -34,6 +36,7 @@ import styles from "./form.module.css"
export default function Form() {
const intl = useIntl()
const lang = useLang()
const pathname = usePathname()
const { confirmationNumber, currencyCode, hotelId, refId, savedCreditCards } =
useMyStayStore((state) => ({
@@ -56,7 +59,7 @@ export default function Form() {
resolver: zodResolver(paymentSchema),
})
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang)}`
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang, isWebview(pathname))}`
const { guaranteeBooking, isLoading, handleGuaranteeError } =
useGuaranteeBooking(refId, false, hotelId)
@@ -93,6 +96,7 @@ export default function Form() {
savedCreditCard ? savedCreditCard.type : PaymentMethodEnum.card,
!!savedCreditCard
)
guaranteeBooking.mutate({
refId,
language: lang,

View File

@@ -8,6 +8,10 @@ export function hotelreservation(lang: Lang) {
return `/${lang}/hotelreservation`
}
export function webviewHotelreservation(lang: Lang) {
return `/${lang}/webview/hotelreservation`
}
export function bookingConfirmation(lang: Lang) {
return `${hotelreservation(lang)}/booking-confirmation`
}
@@ -41,6 +45,8 @@ export function alternativeHotelsMap(lang: Lang) {
return `${hotelreservation(lang)}/alternative-hotels/map`
}
export function guaranteeCallback(lang: Lang) {
return `${hotelreservation(lang)}/gla-payment-callback`
export function guaranteeCallback(lang: Lang, isWebview: boolean) {
return isWebview
? `${webviewHotelreservation(lang)}/gla-payment-callback`
: `${hotelreservation(lang)}/gla-payment-callback`
}