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
213 lines
5.2 KiB
TypeScript
213 lines
5.2 KiB
TypeScript
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 {
|
|
return Object.values<string>(PaymentMethodEnum).includes(value)
|
|
}
|
|
|
|
export function hasFlexibleRate({ room }: RoomState): boolean {
|
|
return room.isFlexRate
|
|
}
|
|
|
|
export function hasPrepaidRate({ room }: RoomState): boolean {
|
|
return !room.isFlexRate
|
|
}
|
|
|
|
export function calculateTotalRoomPrice(
|
|
{ room }: RoomState,
|
|
initialRoomPrice?: number
|
|
) {
|
|
let totalPrice = initialRoomPrice ?? room.roomPrice.perStay.local.price
|
|
|
|
if (room.breakfast) {
|
|
totalPrice += Number(room.breakfast.localPrice.totalPrice) * room.adults
|
|
}
|
|
|
|
if (room.roomFeatures) {
|
|
room.roomFeatures.forEach((pkg) => {
|
|
totalPrice += Number(pkg.localPrice.price)
|
|
})
|
|
}
|
|
|
|
let comparisonPrice = totalPrice
|
|
|
|
const isMember = room.guest.join || room.guest.membershipNo
|
|
if (isMember && "member" in room.roomRate) {
|
|
const publicPrice = room.roomRate.public?.localPrice.pricePerStay ?? 0
|
|
const memberPrice = room.roomRate.member?.localPrice.pricePerStay ?? 0
|
|
const diff = publicPrice - memberPrice
|
|
comparisonPrice = totalPrice + diff
|
|
}
|
|
|
|
return {
|
|
totalPrice,
|
|
comparisonPrice,
|
|
}
|
|
}
|
|
|
|
export const paymentInfoStorageName = "payment-info-storage"
|
|
|
|
type PaymentInfoSessionData = {
|
|
paymentMethod: string
|
|
isSavedCreditCard: boolean
|
|
}
|
|
|
|
export function readPaymentInfoFromSessionStorage():
|
|
| PaymentInfoSessionData
|
|
| undefined {
|
|
try {
|
|
const paymentInfoSessionData = sessionStorage.getItem(
|
|
paymentInfoStorageName
|
|
)
|
|
if (!paymentInfoSessionData) return undefined
|
|
return JSON.parse(paymentInfoSessionData)
|
|
} catch (error) {
|
|
logger.error("Error reading from session storage:", error)
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
export function writePaymentInfoToSessionStorage(
|
|
paymentMethod: string,
|
|
isSavedCreditCard: boolean
|
|
) {
|
|
try {
|
|
sessionStorage.setItem(
|
|
paymentInfoStorageName,
|
|
JSON.stringify({
|
|
paymentMethod,
|
|
isSavedCreditCard,
|
|
})
|
|
)
|
|
} catch (error) {
|
|
logger.error("Error writing to session storage:", error)
|
|
}
|
|
}
|
|
|
|
export function clearPaymentInfoSessionStorage() {
|
|
sessionStorage.removeItem(paymentInfoStorageName)
|
|
}
|
|
|
|
export function mustGuaranteeBooking({
|
|
isUserLoggedIn,
|
|
booking,
|
|
rooms,
|
|
}: {
|
|
isUserLoggedIn: boolean
|
|
booking: { rooms: { counterRateCode?: string }[] }
|
|
rooms: {
|
|
room: {
|
|
memberMustBeGuaranteed?: boolean
|
|
mustBeGuaranteed: boolean
|
|
guest: {
|
|
join: boolean
|
|
membershipNo?: string
|
|
}
|
|
}
|
|
}[]
|
|
}) {
|
|
return rooms.some(({ room }, idx) => {
|
|
if (idx === 0 && isUserLoggedIn && room.memberMustBeGuaranteed) {
|
|
return true
|
|
}
|
|
|
|
if (
|
|
(room.guest.join || room.guest.membershipNo) &&
|
|
booking.rooms[idx].counterRateCode
|
|
) {
|
|
return room.memberMustBeGuaranteed
|
|
}
|
|
|
|
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
|
|
}
|