Merge pull request 'feat: BookingForm → Stripe Checkout' (#64) from feat/wire-stripe-checkout into main
All checks were successful
CI / test (push) Successful in 1m57s
All checks were successful
CI / test (push) Successful in 1m57s
This commit is contained in:
commit
a575d40163
3 changed files with 51 additions and 2 deletions
|
|
@ -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()}
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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<string | null>(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"}
|
||||
</button>
|
||||
|
||||
{!isAuthenticated ? (
|
||||
|
|
@ -200,7 +236,9 @@ export function BookingForm({
|
|||
) : null}
|
||||
|
||||
<p className="text-center text-[11px] text-zinc-500">
|
||||
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."}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue