From 2914e5605ab55e42cca5e6f0cec7d7f8a9d3aa88 Mon Sep 17 00:00:00 2001 From: Claude Integration Date: Mon, 1 Jun 2026 23:35:30 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20BookingForm=20bascule=20sur=20Stripe=20?= =?UTF-8?q?Checkout=20quand=20STRIPE=5FSECRET=5FKEY=20est=20pos=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/carbets/[slug]/page.tsx | 3 ++ src/app/carbets/_components/booking-form.tsx | 42 +++++++++++++++++++- src/lib/stripe.ts | 8 ++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/app/carbets/[slug]/page.tsx b/src/app/carbets/[slug]/page.tsx index 53544fd..f37adae 100644 --- a/src/app/carbets/[slug]/page.tsx +++ b/src/app/carbets/[slug]/page.tsx @@ -12,6 +12,8 @@ import { import { MediaType, UserRole } from "@/generated/prisma/enums"; import { formatAverageRating } from "@/lib/reviews"; +import { isStripeConfigured } from "@/lib/stripe"; + import { BookingForm } from "../_components/booking-form"; import { CarbetGallery } from "../_components/carbet-gallery"; import { CarbetMap } from "../_components/carbet-map"; @@ -255,6 +257,7 @@ export default async function PublicCarbetPage({ params }: PageProps) { minStayNights={carbet.minStayNights} maxStayNights={carbet.maxStayNights} isAuthenticated={Boolean(viewerId)} + stripeEnabled={isStripeConfigured()} /> diff --git a/src/app/carbets/_components/booking-form.tsx b/src/app/carbets/_components/booking-form.tsx index 522a017..816368c 100644 --- a/src/app/carbets/_components/booking-form.tsx +++ b/src/app/carbets/_components/booking-form.tsx @@ -14,6 +14,7 @@ type Props = { minStayNights: number | null; maxStayNights: number | null; isAuthenticated: boolean; + stripeEnabled: boolean; }; function todayPlus(n: number): string { @@ -38,6 +39,7 @@ export function BookingForm({ minStayNights, maxStayNights, isAuthenticated, + stripeEnabled, }: Props) { const router = useRouter(); const [startDate, setStartDate] = useState(null); @@ -88,6 +90,34 @@ export function BookingForm({ setBusy(true); setError(null); try { + if (stripeEnabled) { + // Checkout Stripe : crée la résa + une session Checkout, redirige le user. + const res = await fetch("/api/stripe/checkout/booking", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + carbetId, + startDate, + endDate, + guestCount, + amount: nights * nightlyPrice, + currency: "EUR", + }), + }); + const json = await res.json().catch(() => ({})); + if (!res.ok) { + throw new Error(json?.error || `Erreur ${res.status}`); + } + if (json.checkoutUrl) { + window.location.assign(json.checkoutUrl); + return; + } + // Fallback si pas d'URL retournée → page de la résa créée. + router.push(`/reservations/${json.bookingId ?? ""}`); + return; + } + + // Pas de Stripe configuré → flux direct, résa en PENDING manuel. const res = await fetch("/api/bookings", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -187,7 +217,13 @@ export function BookingForm({ disabled={!canSubmit} className="w-full rounded-md bg-emerald-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-emerald-700 disabled:opacity-50" > - {busy ? "Envoi…" : isAuthenticated ? "Réserver" : "Se connecter pour réserver"} + {busy + ? "Envoi…" + : !isAuthenticated + ? "Se connecter pour réserver" + : stripeEnabled + ? "Payer et réserver" + : "Réserver"} {!isAuthenticated ? ( @@ -200,7 +236,9 @@ export function BookingForm({ ) : null}

- Le créneau est bloqué dès l'envoi. Statut « En attente » jusqu'à confirmation du paiement. + {stripeEnabled + ? "Vous serez redirigé vers Stripe pour le paiement sécurisé." + : "Le créneau est bloqué dès l'envoi. Statut « En attente » jusqu'à confirmation."}

); diff --git a/src/lib/stripe.ts b/src/lib/stripe.ts index adda277..e0d1ca0 100644 --- a/src/lib/stripe.ts +++ b/src/lib/stripe.ts @@ -1,5 +1,13 @@ import Stripe from "stripe"; +/** Détecte si Stripe est utilisable (clé posée + pas un placeholder). */ +export function isStripeConfigured(): boolean { + const key = (process.env.STRIPE_SECRET_KEY ?? "").trim(); + if (!key) return false; + if (key.includes("REPLACE_ME") || key.includes("PLACEHOLDER")) return false; + return key.startsWith("sk_test_") || key.startsWith("sk_live_") || key.startsWith("rk_"); +} + let stripeClient: Stripe | null = null; export function getStripeClient(): Stripe {