"use client" import { zodResolver } from "@hookform/resolvers/zod" import { useRouter, useSearchParams } from "next/navigation" import { useEffect, useMemo, useState } from "react" import { Label as AriaLabel } from "react-aria-components" import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" import { BookingStatusEnum, PAYMENT_METHOD_TITLES, PaymentMethodEnum, } from "@/constants/booking" import { bookingTermsAndConditions, privacyPolicy, } from "@/constants/currentWebHrefs" import { env } from "@/env/client" import { trpc } from "@/lib/trpc/client" import { useEnterDetailsStore } from "@/stores/enter-details" import LoadingSpinner from "@/components/LoadingSpinner" import Button from "@/components/TempDesignSystem/Button" import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" import Link from "@/components/TempDesignSystem/Link" import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import { toast } from "@/components/TempDesignSystem/Toasts" import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus" import useLang from "@/hooks/useLang" import GuaranteeDetails from "./GuaranteeDetails" import PaymentOption from "./PaymentOption" import { PaymentFormData, paymentSchema } from "./schema" import styles from "./payment.module.css" import { PaymentProps } from "@/types/components/hotelReservation/selectRate/section" const maxRetries = 40 const retryInterval = 2000 function isPaymentMethodEnum(value: string): value is PaymentMethodEnum { return Object.values(PaymentMethodEnum).includes(value as PaymentMethodEnum) } export default function Payment({ hotelId, otherPaymentOptions, savedCreditCards, mustBeGuaranteed, }: PaymentProps) { const router = useRouter() const lang = useLang() const intl = useIntl() const queryParams = useSearchParams() const { firstName, lastName, email, phoneNumber, countryCode } = useEnterDetailsStore((state) => state.userData) const [confirmationNumber, setConfirmationNumber] = useState("") const methods = useForm({ defaultValues: { paymentMethod: savedCreditCards?.length ? savedCreditCards[0].id : PaymentMethodEnum.card, smsConfirmation: false, termsAndConditions: false, }, mode: "all", reValidateMode: "onChange", resolver: zodResolver(paymentSchema), }) const initiateBooking = trpc.booking.create.useMutation({ onSuccess: (result) => { if (result?.confirmationNumber) { setConfirmationNumber(result.confirmationNumber) } else { // TODO: add proper error message toast.error("Failed to create booking") } }, onError: (error) => { console.error("Error", error) // TODO: add proper error message toast.error("Failed to create booking") }, }) const bookingStatus = useHandleBookingStatus( confirmationNumber, BookingStatusEnum.PaymentRegistered, maxRetries, retryInterval ) useEffect(() => { if (bookingStatus?.data?.paymentUrl) { router.push(bookingStatus.data.paymentUrl) } }, [bookingStatus, router]) function handleSubmit(data: PaymentFormData) { const allQueryParams = queryParams.size > 0 ? `?${queryParams.toString()}` : "" // set payment method to card if saved card is submitted const paymentMethod = isPaymentMethodEnum(data.paymentMethod) ? data.paymentMethod : PaymentMethodEnum.card const savedCreditCard = savedCreditCards?.find( (card) => card.id === data.paymentMethod ) initiateBooking.mutate({ hotelId: hotelId, checkInDate: "2024-12-10", checkOutDate: "2024-12-11", rooms: [ { adults: 1, childrenAges: [], rateCode: "SAVEEU", roomTypeCode: "QC", guest: { title: "Mr", // TODO: do we need title? firstName, lastName, email, phoneCountryCodePrefix: phoneNumber.slice(0, 3), phoneNumber: phoneNumber.slice(3), countryCode, }, packages: { breakfast: true, allergyFriendly: true, petFriendly: true, accessibility: true, }, smsConfirmationRequested: data.smsConfirmation, }, ], payment: { paymentMethod, card: savedCreditCard ? { alias: savedCreditCard.alias, expiryDate: savedCreditCard.expirationDate, cardType: savedCreditCard.cardType, } : undefined, cardHolder: { email: "test.user@scandichotels.com", name: "Test User", phoneCountryCode: "", phoneSubscriber: "", }, success: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/success`, error: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/error${allQueryParams}`, cancel: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/cancel${allQueryParams}`, }, }) } if ( initiateBooking.isPending || (confirmationNumber && !bookingStatus.data?.paymentUrl) ) { return } const guaranteeing = intl.formatMessage({ id: "guaranteeing" }) const paying = intl.formatMessage({ id: "paying" }) const paymentVerb = mustBeGuaranteed ? guaranteeing : paying return (
{mustBeGuaranteed ? (
{intl.formatMessage({ id: "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.", })}
) : null} {savedCreditCards?.length ? (
{intl.formatMessage({ id: "MY SAVED CARDS" })}
{savedCreditCards?.map((savedCreditCard) => ( ))}
) : null}
{savedCreditCards?.length ? ( {intl.formatMessage({ id: "OTHER PAYMENT METHODS" })} ) : null}
{otherPaymentOptions.map((paymentMethod) => ( ))}
{intl.formatMessage({ id: "I would like to get my booking confirmation via sms", })} {intl.formatMessage( { id: "booking.terms", }, { paymentVerb, termsLink: (str) => ( {str} ), privacyLink: (str) => ( {str} ), } )}
) }