import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import { Section } from '@/components/Section'; import { formatPoundsFromCents } from '@/lib/booking'; import { getBookingCheckoutContext } from '@/lib/payments'; import { site } from '@/lib/site'; type CheckoutPageProps = { params: Promise<{ bookingId: string; }>; }; export async function generateMetadata({ params }: { params: Promise<{ bookingId: string }> }): Promise { const { bookingId } = await params; return { title: `Checkout ${bookingId} | ${site.name}`, description: 'Checkout handoff page for the booking flow.', }; } export default async function BookingCheckoutPage({ params }: CheckoutPageProps) { const { bookingId } = await params; const booking = await getBookingCheckoutContext(bookingId); if (!booking) { notFound(); } return ( <>

Checkout

Finish payment for {booking.property.title}

Review the quote, then continue to Stripe if the session is configured or use the local simulation path in development.

Stay

{booking.property.title}

{booking.arrivalDate.toISOString().slice(0, 10)} to {booking.departureDate.toISOString().slice(0, 10)}

Total

{formatPoundsFromCents(booking.totalCents)}

Current payment state: {booking.payment?.status ?? 'REQUIRES_PAYMENT'}

What happens next

  • Stripe Checkout collects payment when keys are configured.
  • The webhook finalises the payment and booking state.
  • Email notifications are composed from the payment outcome.

Development fallback

If Stripe is not configured in this environment, open the booking status page and use the simulation button to trigger the same webhook finalisation path.

Open booking status
); }