Merged in feat/sw-3642-inject-sas-eb-payment (pull request #3243)
feat(SW-3642): Enable SAS EB payments * Wip add SAS eb payment * Add validate payment call * Check booking status payment method to determine validation * Clean up getPaymentData * Fix PartnerPoints casing * Add comment for validatePartnerPayment error handling * Remove comment Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -8,9 +8,10 @@ import type { LangRoute } from "@scandic-hotels/common/constants/routes/langRout
|
||||
|
||||
import type { BookingFlowVariant } from "./bookingFlowVariants"
|
||||
|
||||
export type RedemptionType = "scandic" | "partner" | "disabled"
|
||||
export type BookingFlowConfig = {
|
||||
bookingCodeEnabled: boolean
|
||||
redemptionEnabled: boolean
|
||||
redemptionType: RedemptionType
|
||||
savedCreditCardsEnabled: boolean
|
||||
enterDetailsMembershipIdInputLocation: "form" | "join-card"
|
||||
variant: BookingFlowVariant
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function RewardNight() {
|
||||
}
|
||||
}, [resetOnMultiroomError, ref])
|
||||
|
||||
if (!config.redemptionEnabled) return null
|
||||
if (config.redemptionType === "disabled") return null
|
||||
|
||||
return (
|
||||
<div ref={ref} onBlur={(e) => closeOnBlur(e.nativeEvent)}>
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function FormContent({
|
||||
const {
|
||||
formState: { errors, isDirty },
|
||||
} = useFormContext<BookingWidgetSchema>()
|
||||
const { bookingCodeEnabled, redemptionEnabled } = useBookingFlowConfig()
|
||||
const { bookingCodeEnabled, redemptionType } = useBookingFlowConfig()
|
||||
const searchParams = useSearchParams()
|
||||
const focusWidget = searchParams.get(FOCUS_WIDGET) === "true"
|
||||
useEffect(() => {
|
||||
@@ -126,7 +126,7 @@ export default function FormContent({
|
||||
)}
|
||||
style={{
|
||||
// TODO: Remove this when redemption is enabled for partner-sas
|
||||
display: redemptionEnabled ? undefined : "none",
|
||||
display: redemptionType !== "disabled" ? undefined : "none",
|
||||
}}
|
||||
>
|
||||
<Voucher />
|
||||
|
||||
@@ -28,7 +28,7 @@ import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||
import { BookingStatusEnum } from "@scandic-hotels/trpc/enums/bookingStatus"
|
||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||
|
||||
import { env } from "../../../../env/client"
|
||||
import { useBookingFlowConfig } from "../../../bookingFlowConfig/bookingFlowConfigContext"
|
||||
import { useBookingFlowContext } from "../../../hooks/useBookingFlowContext"
|
||||
import { clearBookingWidgetState } from "../../../hooks/useBookingWidgetState"
|
||||
import { useHandleBookingStatus } from "../../../hooks/useHandleBookingStatus"
|
||||
@@ -40,9 +40,10 @@ import { writeGlaToSessionStorage } from "./PaymentCallback/helpers"
|
||||
import BookingAlert from "./BookingAlert"
|
||||
import { GuaranteeInfo } from "./GuaranteeInfo"
|
||||
import {
|
||||
getPaymentData,
|
||||
getPaymentMethod,
|
||||
hasFlexibleRate,
|
||||
hasPrepaidRate,
|
||||
isPaymentMethodEnum,
|
||||
mustGuaranteeBooking,
|
||||
writePaymentInfoToSessionStorage,
|
||||
} from "./helpers"
|
||||
@@ -51,7 +52,6 @@ import { getPaymentHeadingConfig } from "./utils"
|
||||
|
||||
import styles from "./payment.module.css"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import type { CreateBookingInput } from "@scandic-hotels/trpc/routers/booking/mutation/create/schema"
|
||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
@@ -77,6 +77,7 @@ export default function PaymentClient({
|
||||
const searchParams = useSearchParams()
|
||||
const { getTopOffset } = useStickyPosition({})
|
||||
const { user, isLoggedIn } = useBookingFlowContext()
|
||||
const { redemptionType } = useBookingFlowConfig()
|
||||
const [refId, setRefId] = useState("")
|
||||
const [isPollingForBookingStatus, setIsPollingForBookingStatus] =
|
||||
useState(false)
|
||||
@@ -273,6 +274,7 @@ export default function PaymentClient({
|
||||
return firstIncompleteRoomIndex !== -1
|
||||
}, [runPreSubmitCallbacks, rooms, methods.formState.errors, getTopOffset])
|
||||
|
||||
const isRedemptionBooking = booking.searchType === SEARCH_TYPE_REDEMPTION
|
||||
const handleSubmit = useCallback(
|
||||
async (data: PaymentFormData) => {
|
||||
setIsSubmitting(true)
|
||||
@@ -283,19 +285,26 @@ export default function PaymentClient({
|
||||
return
|
||||
}
|
||||
|
||||
const paymentMethod = getPaymentMethod(data.paymentMethod, hasFlexRates)
|
||||
|
||||
const savedCreditCard = savedCreditCards?.find(
|
||||
(card) => card.id === data.paymentMethod
|
||||
)
|
||||
|
||||
const guarantee = data.guarantee
|
||||
|
||||
const shouldUsePayment =
|
||||
guarantee || bookingMustBeGuaranteed || !hasOnlyFlexRates
|
||||
const payment = shouldUsePayment
|
||||
? getPaymentData({ paymentMethod, savedCreditCard, lang })
|
||||
: undefined
|
||||
const paymentMethod = getPaymentMethod({
|
||||
paymentMethod: data.paymentMethod,
|
||||
hasFlexRates,
|
||||
isRedemptionBooking,
|
||||
redemptionType,
|
||||
})
|
||||
const payment = getPaymentData({
|
||||
guarantee,
|
||||
bookingMustBeGuaranteed,
|
||||
hasOnlyFlexRates,
|
||||
paymentMethod,
|
||||
savedCreditCard,
|
||||
isRedemptionBooking,
|
||||
lang,
|
||||
})
|
||||
|
||||
const paymentMethodType = savedCreditCard
|
||||
? savedCreditCard.type
|
||||
@@ -315,7 +324,7 @@ export default function PaymentClient({
|
||||
checkOutDate: toDate,
|
||||
hotelId,
|
||||
language: lang,
|
||||
payment,
|
||||
payment: payment ?? undefined,
|
||||
rooms: rooms.map(
|
||||
({ room }, idx): CreateBookingInput["rooms"][number] => {
|
||||
const isMainRoom = idx === 0
|
||||
@@ -414,14 +423,16 @@ export default function PaymentClient({
|
||||
[
|
||||
setIsSubmitting,
|
||||
scrollToInvalidField,
|
||||
hasFlexRates,
|
||||
savedCreditCards,
|
||||
hasFlexRates,
|
||||
redemptionType,
|
||||
bookingMustBeGuaranteed,
|
||||
hasOnlyFlexRates,
|
||||
lang,
|
||||
isRedemptionBooking,
|
||||
hotelId,
|
||||
fromDate,
|
||||
toDate,
|
||||
hotelId,
|
||||
rooms,
|
||||
initiateBooking,
|
||||
isLoggedIn,
|
||||
@@ -440,7 +451,6 @@ export default function PaymentClient({
|
||||
const { preHeading, heading, subHeading, showLearnMore } =
|
||||
getPaymentHeadingConfig(intl, bookingMustBeGuaranteed, hasOnlyFlexRates)
|
||||
|
||||
const isRedemptionBooking = booking.searchType === SEARCH_TYPE_REDEMPTION
|
||||
return (
|
||||
<section
|
||||
className={cx(styles.paymentSection, {
|
||||
@@ -522,22 +532,6 @@ const scrollToElement = (el: HTMLElement, offset: number) => {
|
||||
input?.focus({ preventScroll: true })
|
||||
}
|
||||
|
||||
const getPaymentMethod = (
|
||||
paymentMethod: string | null | undefined,
|
||||
hasFlexRates: boolean
|
||||
): PaymentMethodEnum => {
|
||||
if (hasFlexRates) {
|
||||
return PaymentMethodEnum.card
|
||||
}
|
||||
return paymentMethod && isPaymentMethodEnum(paymentMethod)
|
||||
? paymentMethod
|
||||
: PaymentMethodEnum.card
|
||||
}
|
||||
|
||||
function createPaymentCallbackUrl(lang: Lang) {
|
||||
return `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}/${lang}/hotelreservation/payment-callback`
|
||||
}
|
||||
|
||||
function useBookingStatusRedirect({
|
||||
refId,
|
||||
enabled,
|
||||
@@ -584,32 +578,6 @@ function useBookingStatusRedirect({
|
||||
}, [bookingStatus.data, bookingStatus.isTimeout, router, intl, lang, onError])
|
||||
}
|
||||
|
||||
function getPaymentData({
|
||||
paymentMethod,
|
||||
savedCreditCard,
|
||||
lang,
|
||||
}: {
|
||||
paymentMethod: PaymentMethodEnum
|
||||
savedCreditCard?: CreditCard
|
||||
lang: Lang
|
||||
}) {
|
||||
const paymentRedirectUrl = createPaymentCallbackUrl(lang)
|
||||
|
||||
return {
|
||||
paymentMethod: paymentMethod,
|
||||
success: `${paymentRedirectUrl}/success`,
|
||||
error: `${paymentRedirectUrl}/error`,
|
||||
cancel: `${paymentRedirectUrl}/cancel`,
|
||||
card: savedCreditCard
|
||||
? {
|
||||
alias: savedCreditCard.alias,
|
||||
expiryDate: savedCreditCard.expirationDate,
|
||||
cardType: savedCreditCard.cardType,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function isNotNull<T>(value: T | null): value is T {
|
||||
return value !== null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { mustGuaranteeBooking } from "./helpers"
|
||||
import { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
|
||||
|
||||
import {
|
||||
getPaymentData,
|
||||
getPaymentMethod,
|
||||
mustGuaranteeBooking,
|
||||
} from "./helpers"
|
||||
|
||||
const buildRoom = (
|
||||
overrides: Partial<{
|
||||
@@ -96,3 +103,179 @@ describe("mustGuaranteeBooking", () => {
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getPaymentData", () => {
|
||||
it("returns correct URLs and method when guarantee is true", () => {
|
||||
const result = getPaymentData({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
lang: Lang.en,
|
||||
guarantee: true,
|
||||
bookingMustBeGuaranteed: false,
|
||||
hasOnlyFlexRates: true,
|
||||
isRedemptionBooking: false,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
success: `/en/hotelreservation/payment-callback/success`,
|
||||
error: `/en/hotelreservation/payment-callback/error`,
|
||||
cancel: `/en/hotelreservation/payment-callback/cancel`,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns correct URLs and method when bookingMustBeGuaranteed", () => {
|
||||
const result = getPaymentData({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
lang: Lang.en,
|
||||
guarantee: false,
|
||||
bookingMustBeGuaranteed: true,
|
||||
hasOnlyFlexRates: true,
|
||||
isRedemptionBooking: false,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
success: `/en/hotelreservation/payment-callback/success`,
|
||||
error: `/en/hotelreservation/payment-callback/error`,
|
||||
cancel: `/en/hotelreservation/payment-callback/cancel`,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns correct URLs and method when has only flex rates is false", () => {
|
||||
const result = getPaymentData({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
lang: Lang.en,
|
||||
guarantee: false,
|
||||
bookingMustBeGuaranteed: false,
|
||||
hasOnlyFlexRates: false,
|
||||
isRedemptionBooking: false,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
success: `/en/hotelreservation/payment-callback/success`,
|
||||
error: `/en/hotelreservation/payment-callback/error`,
|
||||
cancel: `/en/hotelreservation/payment-callback/cancel`,
|
||||
})
|
||||
})
|
||||
|
||||
it("returns null when payment isn't required", () => {
|
||||
const result = getPaymentData({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
lang: Lang.en,
|
||||
guarantee: false,
|
||||
bookingMustBeGuaranteed: false,
|
||||
hasOnlyFlexRates: true,
|
||||
isRedemptionBooking: false,
|
||||
})
|
||||
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("returns saved credit card when provided", () => {
|
||||
const result = getPaymentData({
|
||||
paymentMethod: PaymentMethodEnum.card,
|
||||
lang: Lang.en,
|
||||
guarantee: false,
|
||||
bookingMustBeGuaranteed: false,
|
||||
hasOnlyFlexRates: false,
|
||||
isRedemptionBooking: false,
|
||||
savedCreditCard: {
|
||||
alias: "My Visa",
|
||||
expirationDate: "12/25",
|
||||
cardType: "visa",
|
||||
id: "",
|
||||
type: "",
|
||||
truncatedNumber: "",
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
paymentMethod: PaymentMethodEnum.card,
|
||||
success: `/en/hotelreservation/payment-callback/success`,
|
||||
error: `/en/hotelreservation/payment-callback/error`,
|
||||
cancel: `/en/hotelreservation/payment-callback/cancel`,
|
||||
card: {
|
||||
alias: "My Visa",
|
||||
expiryDate: "12/25",
|
||||
cardType: "visa",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("returns correct URLs and method when isRedemptionBooking is true and type is PartnerPoints", () => {
|
||||
const result = getPaymentData({
|
||||
paymentMethod: PaymentMethodEnum.PartnerPoints,
|
||||
lang: Lang.en,
|
||||
guarantee: false,
|
||||
bookingMustBeGuaranteed: false,
|
||||
hasOnlyFlexRates: true,
|
||||
isRedemptionBooking: true,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
paymentMethod: PaymentMethodEnum.PartnerPoints,
|
||||
success: `/en/hotelreservation/payment-callback/success`,
|
||||
error: `/en/hotelreservation/payment-callback/error`,
|
||||
cancel: `/en/hotelreservation/payment-callback/cancel`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("getPaymentMethod", () => {
|
||||
it("returns card when hasFlexRates is true", () => {
|
||||
const hasFlexRates = true
|
||||
const isRedemptionBooking = false
|
||||
const redemptionType = "scandic"
|
||||
const method = getPaymentMethod({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
hasFlexRates,
|
||||
isRedemptionBooking,
|
||||
redemptionType,
|
||||
})
|
||||
|
||||
expect(method).toBe(PaymentMethodEnum.card)
|
||||
})
|
||||
|
||||
it("returns PartnerPoints when is redemption and redemptionType is partner", () => {
|
||||
const hasFlexRates = false
|
||||
const isRedemptionBooking = true
|
||||
const redemptionType = "partner"
|
||||
const method = getPaymentMethod({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
hasFlexRates,
|
||||
isRedemptionBooking,
|
||||
redemptionType,
|
||||
})
|
||||
|
||||
expect(method).toBe(PaymentMethodEnum.PartnerPoints)
|
||||
})
|
||||
|
||||
it("returns paymentMethod when not redemption but redemptionType is partner", () => {
|
||||
const hasFlexRates = false
|
||||
const isRedemptionBooking = false
|
||||
const redemptionType = "partner"
|
||||
const method = getPaymentMethod({
|
||||
paymentMethod: PaymentMethodEnum.swish,
|
||||
hasFlexRates,
|
||||
isRedemptionBooking,
|
||||
redemptionType,
|
||||
})
|
||||
|
||||
expect(method).toBe(PaymentMethodEnum.swish)
|
||||
})
|
||||
|
||||
it("returns card when payment method is string", () => {
|
||||
const hasFlexRates = false
|
||||
const isRedemptionBooking = false
|
||||
const redemptionType = "scandic"
|
||||
const method = getPaymentMethod({
|
||||
paymentMethod: "something-else",
|
||||
hasFlexRates,
|
||||
isRedemptionBooking,
|
||||
redemptionType,
|
||||
})
|
||||
|
||||
expect(method).toBe(PaymentMethodEnum.card)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
|
||||
import { logger } from "@scandic-hotels/common/logger"
|
||||
|
||||
import { env } from "../../../../env/client"
|
||||
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||
|
||||
import type { RedemptionType } from "../../../bookingFlowConfig/bookingFlowConfig"
|
||||
import type { RoomState } from "../../../stores/enter-details/types"
|
||||
|
||||
export function isPaymentMethodEnum(value: string): value is PaymentMethodEnum {
|
||||
@@ -123,3 +129,84 @@ export function mustGuaranteeBooking({
|
||||
return room.mustBeGuaranteed
|
||||
})
|
||||
}
|
||||
|
||||
function createPaymentCallbackUrl(lang: Lang) {
|
||||
return `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}/${lang}/hotelreservation/payment-callback`
|
||||
}
|
||||
|
||||
export function getPaymentData({
|
||||
guarantee,
|
||||
bookingMustBeGuaranteed,
|
||||
hasOnlyFlexRates,
|
||||
paymentMethod,
|
||||
isRedemptionBooking,
|
||||
savedCreditCard,
|
||||
lang,
|
||||
}: {
|
||||
guarantee: boolean
|
||||
bookingMustBeGuaranteed: boolean
|
||||
hasOnlyFlexRates: boolean
|
||||
paymentMethod: PaymentMethodEnum
|
||||
isRedemptionBooking: boolean
|
||||
savedCreditCard?: CreditCard
|
||||
lang: Lang
|
||||
}) {
|
||||
const paymentRedirectUrl = createPaymentCallbackUrl(lang)
|
||||
const redirectUrls = {
|
||||
success: `${paymentRedirectUrl}/success`,
|
||||
error: `${paymentRedirectUrl}/error`,
|
||||
cancel: `${paymentRedirectUrl}/cancel`,
|
||||
}
|
||||
|
||||
if (
|
||||
isRedemptionBooking &&
|
||||
paymentMethod === PaymentMethodEnum.PartnerPoints
|
||||
) {
|
||||
return {
|
||||
paymentMethod: paymentMethod,
|
||||
...redirectUrls,
|
||||
}
|
||||
}
|
||||
|
||||
const shouldUsePayment =
|
||||
guarantee || bookingMustBeGuaranteed || !hasOnlyFlexRates
|
||||
if (!shouldUsePayment) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
paymentMethod: paymentMethod,
|
||||
...redirectUrls,
|
||||
card: savedCreditCard
|
||||
? {
|
||||
alias: savedCreditCard.alias,
|
||||
expiryDate: savedCreditCard.expirationDate,
|
||||
cardType: savedCreditCard.cardType,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export const getPaymentMethod = ({
|
||||
paymentMethod,
|
||||
hasFlexRates,
|
||||
isRedemptionBooking,
|
||||
redemptionType,
|
||||
}: {
|
||||
paymentMethod: string | null | undefined
|
||||
hasFlexRates: boolean
|
||||
isRedemptionBooking: boolean
|
||||
redemptionType: RedemptionType
|
||||
}): PaymentMethodEnum => {
|
||||
if (isRedemptionBooking && redemptionType === "partner") {
|
||||
return PaymentMethodEnum.PartnerPoints
|
||||
}
|
||||
|
||||
if (hasFlexRates) {
|
||||
return PaymentMethodEnum.card
|
||||
}
|
||||
|
||||
return paymentMethod && isPaymentMethodEnum(paymentMethod)
|
||||
? paymentMethod
|
||||
: PaymentMethodEnum.card
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export async function EnterDetailsPage({
|
||||
|
||||
// This should never happen unless a user tampers with the URL
|
||||
if (
|
||||
!config.redemptionEnabled &&
|
||||
config.redemptionType === "disabled" &&
|
||||
booking.searchType === SEARCH_TYPE_REDEMPTION
|
||||
) {
|
||||
throw new Error("Redemptions are disabled")
|
||||
|
||||
@@ -19,6 +19,8 @@ import { serverClient } from "../trpc"
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
import type { NextSearchParams } from "../types"
|
||||
import { CreateBookingSchema } from "@scandic-hotels/trpc/routers/booking/mutation/create/schema"
|
||||
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
|
||||
|
||||
type PaymentCallbackPageProps = {
|
||||
lang: Lang
|
||||
@@ -48,11 +50,28 @@ export async function PaymentCallbackPage({
|
||||
}
|
||||
|
||||
const returnUrl = details(lang)
|
||||
const searchObject = new URLSearchParams()
|
||||
let errorMessage = undefined
|
||||
|
||||
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
|
||||
@@ -81,14 +100,24 @@ export async function PaymentCallbackPage({
|
||||
}
|
||||
|
||||
const booking = await getBooking(confirmationNumber, lang, token)
|
||||
|
||||
const refId = booking?.refId
|
||||
|
||||
if (
|
||||
status === PaymentCallbackStatusEnum.Success &&
|
||||
confirmationNumber &&
|
||||
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 })
|
||||
}
|
||||
|
||||
const expire = Math.floor(Date.now() / 1000) + 60
|
||||
const sig = encrypt(expire.toString())
|
||||
const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(refId)}`
|
||||
@@ -108,49 +137,67 @@ export async function PaymentCallbackPage({
|
||||
)
|
||||
}
|
||||
|
||||
if (refId) {
|
||||
try {
|
||||
const caller = await serverClient()
|
||||
const bookingStatus = await caller.booking.status({
|
||||
refId,
|
||||
})
|
||||
return (
|
||||
<HandleBookingStatusError
|
||||
status={status}
|
||||
booking={bookingStatus?.booking ?? null}
|
||||
returnUrl={returnUrl.toString()}
|
||||
confirmationNumber={confirmationNumber}
|
||||
config={config}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const { booking } = bookingStatus
|
||||
|
||||
// TODO: how to handle errors for multiple rooms?
|
||||
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(
|
||||
`[payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
|
||||
)
|
||||
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
|
||||
errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
|
||||
}
|
||||
}
|
||||
|
||||
if (status === PaymentCallbackStatusEnum.Error) {
|
||||
function HandleBookingStatusError({
|
||||
booking,
|
||||
confirmationNumber,
|
||||
returnUrl,
|
||||
config,
|
||||
status,
|
||||
}: {
|
||||
booking: CreateBookingSchema | null
|
||||
confirmationNumber?: string
|
||||
returnUrl: string
|
||||
config: BookingFlowConfig
|
||||
status: PaymentCallbackStatusEnum
|
||||
}) {
|
||||
if (!booking) {
|
||||
logger.error(
|
||||
`[payment-callback] error status received for ${confirmationNumber}, status: ${status}`
|
||||
`[payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
|
||||
)
|
||||
const searchObject = new URLSearchParams()
|
||||
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
|
||||
errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
|
||||
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.toString()}
|
||||
returnUrl={returnUrl}
|
||||
searchObject={searchObject}
|
||||
status={status}
|
||||
errorMessage={errorMessage}
|
||||
|
||||
Reference in New Issue
Block a user