refund and delayed charge
This commit is contained in:
128
frontend/src/components/EmbeddedStripeCheckout.tsx
Normal file
128
frontend/src/components/EmbeddedStripeCheckout.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { loadStripe } from "@stripe/stripe-js";
|
||||
import {
|
||||
EmbeddedCheckoutProvider,
|
||||
EmbeddedCheckout,
|
||||
} from "@stripe/react-stripe-js";
|
||||
import { stripeAPI, rentalAPI } from "../services/api";
|
||||
|
||||
const stripePromise = loadStripe(
|
||||
process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY || ""
|
||||
);
|
||||
|
||||
interface EmbeddedStripeCheckoutProps {
|
||||
rentalData: any;
|
||||
onSuccess: () => void;
|
||||
onError: (error: string) => void;
|
||||
}
|
||||
|
||||
const EmbeddedStripeCheckout: React.FC<EmbeddedStripeCheckoutProps> = ({
|
||||
rentalData,
|
||||
onSuccess,
|
||||
onError,
|
||||
}) => {
|
||||
const [clientSecret, setClientSecret] = useState<string>("");
|
||||
const [creating, setCreating] = useState(false);
|
||||
const [sessionId, setSessionId] = useState<string>("");
|
||||
|
||||
const createCheckoutSession = useCallback(async () => {
|
||||
try {
|
||||
setCreating(true);
|
||||
|
||||
const response = await stripeAPI.createSetupCheckoutSession({
|
||||
rentalData
|
||||
});
|
||||
|
||||
setClientSecret(response.data.clientSecret);
|
||||
setSessionId(response.data.sessionId);
|
||||
} catch (error: any) {
|
||||
onError(
|
||||
error.response?.data?.error || "Failed to create checkout session"
|
||||
);
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
}, [rentalData, onError]);
|
||||
|
||||
useEffect(() => {
|
||||
createCheckoutSession();
|
||||
}, [createCheckoutSession]);
|
||||
|
||||
const handleComplete = useCallback(() => {
|
||||
// For embedded checkout, we need to retrieve the session to get payment method
|
||||
(async () => {
|
||||
try {
|
||||
if (!sessionId) {
|
||||
throw new Error("No session ID available");
|
||||
}
|
||||
|
||||
// Get the completed checkout session
|
||||
const sessionResponse = await stripeAPI.getCheckoutSession(sessionId);
|
||||
const { status: sessionStatus, setup_intent } = sessionResponse.data;
|
||||
|
||||
if (sessionStatus !== "complete") {
|
||||
throw new Error("Payment setup was not completed");
|
||||
}
|
||||
|
||||
if (!setup_intent?.payment_method) {
|
||||
throw new Error("No payment method found in setup intent");
|
||||
}
|
||||
|
||||
// Extract payment method ID - handle both string ID and object cases
|
||||
const paymentMethodId = typeof setup_intent.payment_method === 'string'
|
||||
? setup_intent.payment_method
|
||||
: setup_intent.payment_method.id;
|
||||
|
||||
if (!paymentMethodId) {
|
||||
throw new Error("No payment method ID found");
|
||||
}
|
||||
|
||||
// Create the rental with the payment method ID
|
||||
const rentalPayload = {
|
||||
...rentalData,
|
||||
stripePaymentMethodId: paymentMethodId
|
||||
};
|
||||
|
||||
await rentalAPI.createRental(rentalPayload);
|
||||
onSuccess();
|
||||
} catch (error: any) {
|
||||
onError(error.response?.data?.error || error.message || "Failed to complete rental request");
|
||||
}
|
||||
})();
|
||||
}, [sessionId, rentalData, onSuccess, onError]);
|
||||
|
||||
if (creating) {
|
||||
return (
|
||||
<div className="text-center py-4">
|
||||
<div className="spinner-border mb-3" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p>Preparing secure checkout...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!clientSecret) {
|
||||
return (
|
||||
<div className="text-center py-4">
|
||||
<p className="text-muted">Unable to load checkout</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="embedded-checkout">
|
||||
<EmbeddedCheckoutProvider
|
||||
stripe={stripePromise}
|
||||
options={{
|
||||
clientSecret,
|
||||
onComplete: handleComplete
|
||||
}}
|
||||
>
|
||||
<EmbeddedCheckout />
|
||||
</EmbeddedCheckoutProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmbeddedStripeCheckout;
|
||||
Reference in New Issue
Block a user