payment for rental from renter stripe integration

This commit is contained in:
jackiettran
2025-08-27 19:46:27 -04:00
parent 601e11b7e8
commit 38346bec27
13 changed files with 1090 additions and 421 deletions

View File

@@ -0,0 +1,247 @@
import React, { useState, useEffect, useRef } from "react";
import { useSearchParams, useNavigate } from "react-router-dom";
import { stripeAPI, rentalAPI } from "../services/api";
const CheckoutReturn: React.FC = () => {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const [status, setStatus] = useState<
"loading" | "success" | "error" | "failed" | "rental_error"
>("loading");
const [error, setError] = useState<string | null>(null);
const [processing, setProcessing] = useState(false);
const hasProcessed = useRef(false);
useEffect(() => {
if (hasProcessed.current) return;
const sessionId = searchParams.get("session_id");
if (!sessionId) {
setStatus("error");
setError("No session ID found in URL");
return;
}
hasProcessed.current = true;
checkSessionStatus(sessionId);
}, [searchParams]);
const createRental = async () => {
try {
// Get rental data from localStorage (set before payment)
const rentalDataString = localStorage.getItem("pendingRental");
if (!rentalDataString) {
console.error("No rental data found in localStorage");
throw new Error("No rental data found in localStorage");
}
const rentalData = JSON.parse(rentalDataString);
const response = await rentalAPI.createRental(rentalData);
// Clear the pending rental data
localStorage.removeItem("pendingRental");
localStorage.removeItem("lastItemId");
return response;
} catch (error: any) {
const errorMessage =
error.response?.data?.message ||
error.message ||
"Failed to create rental";
console.error("Throwing error:", errorMessage);
throw new Error(errorMessage);
}
};
const checkSessionStatus = async (sessionId: string) => {
try {
setProcessing(true);
// Get checkout session status
const response = await stripeAPI.getCheckoutSession(sessionId);
const { status: sessionStatus, payment_status } = response.data;
if (sessionStatus === "complete" && payment_status === "paid") {
// Payment was successful - now create the rental
try {
const rentalResult = await createRental();
setStatus("success");
} catch (rentalError: any) {
// Payment succeeded but rental creation failed
setStatus("rental_error");
setError(rentalError.message || "Failed to create rental record");
}
} else if (sessionStatus === "open") {
// Payment was not completed
setStatus("failed");
setError("Payment was not completed. Please try again.");
} else {
setStatus("error");
setError("Payment failed or was cancelled.");
}
} catch (error: any) {
setStatus("error");
setError(
error.response?.data?.error || "Failed to verify payment status"
);
} finally {
setProcessing(false);
}
};
const handleRetry = () => {
// Go back to the item page to try payment again
const itemId = localStorage.getItem("lastItemId");
if (itemId) {
navigate(`/items/${itemId}`);
} else {
navigate("/");
}
};
const handleRetryRentalCreation = async () => {
setProcessing(true);
try {
await createRental();
setStatus("success");
setError(null);
} catch (error: any) {
setError(error.message || "Failed to create rental record");
} finally {
setProcessing(false);
}
};
const handleGoToRentals = () => {
navigate("/my-rentals");
};
if (status === "loading" || processing) {
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-6 text-center">
<div className="spinner-border mb-3" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<h3>Processing your payment...</h3>
<p className="text-muted">
Please wait while we confirm your payment and set up your rental.
</p>
</div>
</div>
</div>
);
}
if (status === "success") {
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-6 text-center">
<div className="alert alert-success p-4">
<i className="bi bi-check-circle-fill display-1 text-success mb-3"></i>
<h2>Payment Successful!</h2>
<p className="mb-4">
Your rental has been confirmed. You can view the details in your
rentals page.
</p>
<div className="d-grid gap-2 d-md-block">
<button
className="btn btn-primary btn-lg me-2"
onClick={handleGoToRentals}
>
View My Rentals
</button>
<button
className="btn btn-outline-secondary btn-lg"
onClick={() => navigate("/")}
>
Continue Browsing
</button>
</div>
</div>
</div>
</div>
</div>
);
}
if (status === "rental_error") {
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-6 text-center">
<div className="alert alert-warning p-4">
<i className="bi bi-exclamation-triangle-fill display-1 text-warning mb-3"></i>
<h2>Payment Successful - Rental Setup Issue</h2>
<p className="mb-4">
Your payment was processed successfully, but we encountered an
issue creating your rental record:
<br />
<strong>{error}</strong>
</p>
<div className="d-grid gap-2 d-md-block">
<button
className="btn btn-primary btn-lg me-2"
onClick={handleRetryRentalCreation}
disabled={processing}
>
{processing ? "Retrying..." : "Retry Rental Creation"}
</button>
<button
className="btn btn-outline-secondary btn-lg"
onClick={() => navigate("/")}
>
Contact Support
</button>
</div>
</div>
</div>
</div>
</div>
);
}
if (status === "failed" || status === "error") {
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-6 text-center">
<div className="alert alert-danger p-4">
<i className="bi bi-exclamation-triangle-fill display-1 text-danger mb-3"></i>
<h2>
{status === "failed" ? "Payment Incomplete" : "Payment Error"}
</h2>
<p className="mb-4">
{error || "There was an issue processing your payment."}
</p>
<div className="d-grid gap-2 d-md-block">
<button
className="btn btn-primary btn-lg me-2"
onClick={handleRetry}
>
Try Again
</button>
<button
className="btn btn-outline-secondary btn-lg"
onClick={() => navigate("/")}
>
Go Home
</button>
</div>
</div>
</div>
</div>
</div>
);
}
return null;
};
export default CheckoutReturn;