"use client"; // Stripe Payment Element for the booking-deposit flow. // First real client component in the app — needed because Stripe.js can only // run in the browser. import { useState } from "react"; import { loadStripe, type Stripe } from "@stripe/stripe-js"; import { Elements, PaymentElement, useElements, useStripe, } from "@stripe/react-stripe-js"; // Cache the Stripe object across renders. loadStripe returns a Promise-like. let stripePromise: ReturnType | null = null; function getStripe(publishableKey: string) { if (!stripePromise) stripePromise = loadStripe(publishableKey); return stripePromise; } export function PaymentForm({ publishableKey, clientSecret, amountCents, returnUrl, }: { publishableKey: string; clientSecret: string; amountCents: number; returnUrl: string; }) { return ( ); } function CheckoutInner({ amountCents, returnUrl, }: { amountCents: number; returnUrl: string; }) { const stripe = useStripe(); const elements = useElements(); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); async function onSubmit(e: React.FormEvent) { e.preventDefault(); if (!stripe || !elements) return; setSubmitting(true); setError(null); const { error: stripeError } = await stripe.confirmPayment({ elements, confirmParams: { return_url: returnUrl }, }); // confirmPayment only returns here on validation/network errors; // on success Stripe redirects to return_url. if (stripeError) { setError(stripeError.message ?? "Payment failed — try again."); setSubmitting(false); } } return (
{error && (
{error}
)}

Your card is charged the deposit now. The remaining balance is paid in person.

); } // Type re-export so the page can declare props without importing @stripe/stripe-js. export type { Stripe };