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 ( ) } 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 ( ) } 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 ( ) } return ( ) } 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 ( ) } // 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 ( ) }