Files
web/packages/booking-flow/lib/pages/PaymentCallbackPage.tsx
Anton Gunnarsson 3e3b15940f Merged in fix/booking-flow-eslint-fix (pull request #3342)
fix: Upgrade booking-flow eslint config

* Upgrade booking-flow eslint config


Approved-by: Bianca Widstam
2025-12-12 11:40:45 +00:00

209 lines
6.2 KiB
TypeScript

import { notFound } from "next/navigation"
import { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import {
bookingConfirmation,
details,
} from "@scandic-hotels/common/constants/routes/hotelReservation"
import { logger } from "@scandic-hotels/common/logger"
import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { getBooking } from "@scandic-hotels/trpc/routers/booking/utils"
import { encrypt } from "@scandic-hotels/trpc/utils/encryption"
import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
import { HandleErrorCallback } from "../components/EnterDetails/Payment/PaymentCallback/HandleErrorCallback"
import { HandleSuccessCallback } from "../components/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback"
import { serverClient } from "../trpc"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { CreateBookingSchema } from "@scandic-hotels/trpc/routers/booking/mutation/create/schema"
import type { NextSearchParams } from "../types"
type PaymentCallbackPageProps = {
lang: Lang
searchParams: NextSearchParams
userAccessToken: string | null
status: PaymentCallbackStatusEnum
config: BookingFlowConfig
}
export async function PaymentCallbackPage({
lang,
userAccessToken,
searchParams,
status,
config,
}: PaymentCallbackPageProps) {
const { confirmationNumber } = searchParams
if (
!status ||
!confirmationNumber ||
typeof confirmationNumber !== "string"
) {
logger.error(
`[payment-callback] missing status or confirmationNumber in search params`
)
notFound()
}
const returnUrl = details(lang)
if (status === PaymentCallbackStatusEnum.Cancel) {
const searchObject = new URLSearchParams()
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionCancelled)
return (
<BookingFlowConfig config={config}>
<HandleErrorCallback
returnUrl={returnUrl.toString()}
searchObject={searchObject}
status={status}
/>
</BookingFlowConfig>
)
}
if (status === PaymentCallbackStatusEnum.Error) {
logger.error(
`[payment-callback] error status received for ${confirmationNumber}, status: ${status}`
)
const searchObject = new URLSearchParams()
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
const errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
return (
<BookingFlowConfig config={config}>
<HandleErrorCallback
returnUrl={returnUrl.toString()}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
</BookingFlowConfig>
)
}
let token = userAccessToken
if (!token) {
const serviceToken = await getServiceToken()
if (serviceToken) {
token = serviceToken.access_token
}
}
if (!token) {
logger.error(
`[payment-callback] no token found for user, cannot fetch booking`
)
notFound()
}
const booking = await getBooking(confirmationNumber, lang, token)
const refId = booking?.refId
const caller = await serverClient()
const bookingStatus = refId
? await caller.booking.status({
refId,
})
: null
if (status === PaymentCallbackStatusEnum.Success && refId) {
const shouldValidatePayment =
bookingStatus?.booking.paymentMethod === PaymentMethodEnum.PartnerPoints
if (shouldValidatePayment) {
// TODO We probably need better error handling for this mutation,
// but for now we've just implemented the happy path
await caller.booking.validatePartnerPayment({ confirmationNumber })
}
// eslint-disable-next-line react-hooks/purity
const expire = Math.floor(Date.now() / 1000) + 60
const sig = encrypt(expire.toString())
const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(refId)}`
logger.debug(
`[payment-callback] rendering success callback with confirmation number: ${confirmationNumber}`
)
return (
<BookingFlowConfig config={config}>
<HandleSuccessCallback
refId={refId}
sig={sig}
successRedirectUrl={confirmationUrl}
cardType={booking.guaranteeInfo?.cardType}
/>
</BookingFlowConfig>
)
}
return (
<HandleBookingStatusError
status={status}
booking={bookingStatus?.booking ?? null}
returnUrl={returnUrl.toString()}
confirmationNumber={confirmationNumber}
config={config}
/>
)
}
function HandleBookingStatusError({
booking,
confirmationNumber,
returnUrl,
config,
status,
}: {
booking: CreateBookingSchema | null
confirmationNumber?: string
returnUrl: string
config: BookingFlowConfig
status: PaymentCallbackStatusEnum
}) {
if (!booking) {
logger.error(
`[payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
)
const searchObject = new URLSearchParams()
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
const errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
return (
<BookingFlowConfig config={config}>
<HandleErrorCallback
returnUrl={returnUrl}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
</BookingFlowConfig>
)
}
// TODO: how to handle errors for multiple rooms?
const error = booking.errors.find((e) => e.errorCode)
const errorMessage =
error?.description ??
`No error message found for booking ${confirmationNumber}, status: ${status}`
const searchObject = new URLSearchParams()
searchObject.set(
"errorCode",
error ? error.errorCode.toString() : BookingErrorCodeEnum.TransactionFailed
)
return (
<BookingFlowConfig config={config}>
<HandleErrorCallback
returnUrl={returnUrl}
searchObject={searchObject}
status={status}
errorMessage={errorMessage}
/>
</BookingFlowConfig>
)
}